function varargout = PIDBasics(varargin)
% PIDBASICS MATLAB code for PIDBasics.fig
% PIDBASICS, by itself, creates a new PIDBASICS or raises the existing
% singleton*.
%
% H = PIDBASICS returns the handle to a new PIDBASICS or the handle to
% the existing singleton*.
%
% PIDBASICS('CALLBACK',hObject,eventData,handles,...) calls the local
% function named CALLBACK in PIDBASICS.M with the given input arguments.
%
% PIDBASICS('Property','Value',...) creates a new PIDBASICS or raises the
% existing singleton*. Starting from the left, property value pairs are
% applied to the GUI before PIDBasics_OpeningFcn gets called. An
% unrecognized property name or invalid value makes property application
% stop. All inputs are passed to PIDBasics_OpeningFcn via varargin.
%
% *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one
% instance to run (singleton)".
%
% Copyright 2012 The MathWorks, Inc
% See also: GUIDE, GUIDATA, GUIHANDLES
% Edit the above text to modify the response to help PIDBasics
% Last Modified by GUIDE v2.5 03-Aug-2012 11:49:03
% Checking that Control System Toolbox(TM) is available
if isempty(ver('control'))
errordlg('You need Control System Toolbox to run this GUI', ...
'Toolbox missing')
return;
end
% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name', mfilename, ...
'gui_Singleton', gui_Singleton, ...
'gui_OpeningFcn', @PIDBasics_OpeningFcn, ...
'gui_OutputFcn', @PIDBasics_OutputFcn, ...
'gui_LayoutFcn', [] , ...
'gui_Callback', []);
if nargin && ischar(varargin{1})
gui_State.gui_Callback = str2func(varargin{1});
end
if nargout
[varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT
% --- Executes just before PIDBasics is made visible.
function PIDBasics_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% varargin command line arguments to PIDBasics (see VARARGIN)
% Choose default command line output for PIDBasics
handles.output = hObject;
% Default process creation
G = zpk([],[-1 -2 -4 -8],64);
set(G,'DisplayFormat','Time');
handles.G = G;
tfprocessText = displayTfProcess(G);
set(handles.text_tf,'String',tfprocessText)
% Setting DC gain for default process
Kp = computeDcGain(G);
set(handles.text_Kp, 'String', sprintf('Kp: %0.2f', Kp))
set(handles.slider_Kp, 'Min', log10(Kp/4), 'Max', log10(Kp*4), ...
'Value', log10(Kp))
% Setting additional parameters
handles.params.Tfinal = 50; % Final time
handles.params.freq = 11; % Number of t points per second
handles.params.setpoint = 1; % Setpoint magnitude
handles.params.distMag = 0.9; % Load disturbance magnitude
handles.params.distTime = 17; % Load disturbance time
handles.params.noiseVar = 0.6; % Noise variance
handles.params.noiseTime = 33; % Noise time
handles.params.sqwdx = -0.025; % X-axis plot margin
handles.params.sqwdy = -0.1; % Y-axis plot margin
% Default controller creation: a P-Controller
enableDisableSlider({'on','off','off','off'}, handles)
handles = setDefaultControllerValues(handles);
K = power(10, get(handles.slider_K, 'Value'));
handles.C = pidstd(K);
handles.warn = [];
% Initial process and controller output plots.
handles = processAndControllerPlots(handles, true, true);
% Robustness and Performance metrics
handles = robustnessMetrics(handles);
handles = performanceMetrics(handles);
% Handle creation for datatips
color = [1,0,0];
handles.cursor(1) = plot(handles.processOutput, NaN, NaN, ...
'Color', color, 'Marker', 's', 'MarkerFaceColor', color, ...
'MarkerSize', 4);
handles.cursor(2) = plot(handles.controllerOutput, NaN, NaN, ...
'Color', color, 'Marker', 's', 'MarkerFaceColor', color, ...
'MarkerSize', 4);
handles.hText(1) = text(NaN, NaN, '', 'Parent', handles.processOutput, ...
'BackgroundColor', 'none', 'Color', [0,0,0], 'FontSize', 8);
handles.hText(2) = text(NaN, NaN, '', ...
'Parent', handles.controllerOutput, ...
'BackgroundColor', 'none', 'Color', [0,0,0], 'FontSize', 8);
% Plot titles and labels
title(handles.processOutput, 'Process Output', ...
'HorizontalAlignment', 'left', 'Units', 'normalized', ...
'Position', [0.025 1.025]);
title(handles.controllerOutput, 'Controller Output', ...
'HorizontalAlignment', 'left', 'Units', 'normalized', ...
'Position', [0.025 1.025]);
xlabel(handles.processOutput, 'Time', ...
'HorizontalAlignment', 'right', 'Units', 'normalized', ...
'Position', [0.975 -0.05]);
xlabel(handles.controllerOutput, 'Time', ...
'HorizontalAlignment', 'right', 'Units', 'normalized', ...
'Position', [0.975 -0.05]);
ylabel(handles.processOutput, 'y', ...
'HorizontalAlignment', 'right', 'Units', 'normalized', ...
'Position', [-0.03 0.9], 'Rotation', 0);
ylabel(handles.controllerOutput, 'u', ...
'HorizontalAlignment', 'right', 'Units', 'normalized', ...
'Position', [-0.03 0.9], 'Rotation', 0);
% Setting callback for selection change in the Controller buttongroup
set(handles.controller, 'SelectionChangeFcn', ...
{@controller_Change, handles})
% Setting sliders listeners
addlistener(handles.slider_Kp, 'ContinuousValueChange', ...
@(src,evnt)slider_Kp_Scroll(src,evnt,handles));
addlistener(handles.slider_K, 'ContinuousValueChange',...
@(src,evnt)slider_Gains_Scroll(src,evnt,handles.text_K));
addlistener(handles.slider_Ti, 'ContinuousValueChange',...
@(src,evnt)slider_Gains_Scroll(src,evnt,handles.text_Ti));
addlistener(handles.slider_Td, 'ContinuousValueChange',...
@(src,evnt)slider_Gains_Scroll(src,evnt,handles.text_Td));
addlistener(handles.slider_N, 'ContinuousValueChange',...
@(src,evnt)slider_Gains_Scroll(src,evnt,handles.text_N));
% Update handles structure
guidata(hObject, handles);
% UIWAIT makes PIDBasics wait for user response (see UIRESUME)
% uiwait(handles.PIDBasics);
% --- Outputs from this function are returned to the command line.
function varargout = PIDBasics_OutputFcn(hObject, eventdata, handles)
% varargout cell array for returning output args (see VARARGOUT);
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Get default command line output from handles structure
varargout{1} = handles.output;
% --- Updates handles with default values for the controller sliders.
function handles = setDefaultControllerValues(handles)
hSlider = {'slider_K', 'slider_Ti', 'slider_Td', 'slider_N'};
hText = {'text_K', 'text_Ti', 'text_Td', 'text_N'};
value = [1,1,1,10]; % Logarithmic values are computed so that the slider
% remains in the middle
controllers = get(handles.controller,'Children');
controllerP = controllers(strcmp(get(get(handles.controller,'Children'),...
'Tag'),'controller_P'));
set(handles.controller,'SelectedObject',controllerP)
for i = 1:length(hSlider)
set(handles.(hSlider{i}), 'Min', log10(value(i)/4), ...
'Max', log10(value(i)*4), 'Value', log10(value(i)));
str = textStr(handles.(hText{i}));
set(handles.(hText{i}), 'String', sprintf([str, '%0.2f'], value(i)))
end
% --- Auxiliary function to set the string for some static text parameters.
function str = textStr(hText)
str = get(hText,'String');
str = str(1:regexp(str,':')+1);
% --- Updates handles with the new selected controller.
function handles = controllerUpdate(handles)
% Uibuttongroup selection
selectedRB = get(get(handles.controller,'SelectedObject'),'String');
switch selectedRB
case{'P'}
K = power(10,get(handles.slider_K, 'Value'));
handles.C = pidstd(K);
case{'I'}
Ti = power(10,get(handles.slider_Ti, 'Value'));
s = tf('s');
handles.C = 1/(Ti*s);
case{'PI'}
K = power(10,get(handles.slider_K, 'Value'));
Ti = power(10,get(handles.slider_Ti, 'Value'));
handles.C = pidstd(K,Ti);
case{'PD'}
K = power(10,get(handles.slider_K, 'Value'));
Td = power(10,get(handles.slider_Td, 'Value'));
N = power(10,get(handles.slider_N, 'Value'));
handles.C = pidstd(K,Inf,Td,N);
case{'PID'}
K = power(10,get(handles.slider_K, 'Value'));
Td = power(10,get(handles.slider_Td, 'Value'));
Ti = power(10,get(handles.slider_Ti, 'Value'));
N = power(10,get(handles.slider_N, 'Value'));
handles.C = pidstd(K,Ti,Td,N);
end
% --- Process and controller plots based on current plant and controller.
function handles = processAndControllerPlots(handles, newPlot, moveAxes)
t = linspace(0, handles.params.Tfinal, ...
handles.params.freq*handles.params.Tfinal);
% Process and controller outputs
% y = (C*G)/(1+C*G) r + G/(1+C*G) d - (C*G)/(1+C*G) n
% u = C/(1+C*G) r - (C*G)/(1+C*G)d - C/(1+C*G) n
T = feedback(handles.C*handles.G,1); % T -> (C*G)/(1+C*G)
TC = T/handles.C;
TG = T/handles.G;
% Step response: r
r = handles.params.setpoint * ones(length(t),1);
[y1,ty] = lsim(T , r, t); % (C*G)/(1+C*G)
[u1,tu] = lsim(TG, r, t); % C/(1+C*G)
% Disturbance: d
d = zeros(length(t),1);
[~,iTimeMag] = min(abs(t - handles.params.distTime));
d(iTimeMag:end) = d(iTimeMag:end) + handles.params.distMag;
y2 = lsim(TC, d, t); % G/(1+C*G)
u2 = lsim(-T, d, t); % -((C*G)/(1+C*G))
% Noise: n
n = zeros(length(t),1);
[~,iTimeNoise] = min(abs(t - handles.params.noiseTime));
n(iTimeNoise:end) = n(iTimeNoise:end) + ...
handles.params.noiseVar * 0.05* randn(length(n)-iTimeNoise+1,1);
y3 = lsim(-T , n, t); % -(G*C/(1+C*G))
u3 = lsim(-TG, n, t); % -(C/(1+C*G))
y = y1 + y2 + y3 + n;
u = u1 + u2 + u3;
% Performance metrics. Computed here for later use.
handles.params.performance.sigmau = std(u3);
[~,iOvershootTime] = max(y1);
handles.params.performance.overshootTime = t(iOvershootTime);
% Minimum and maximum values for X and Y axes
minT1 = min(ty); minY = min(y);
maxT1 = max(ty); maxY = max([y;handles.params.setpoint;...
handles.params.distMag;handles.params.noiseVar]);
minT2 = min(tu); minU = min(0,min(u));
maxT2 = max(tu); maxU = max([u;handles.params.setpoint;...
handles.params.distMag;handles.params.noiseVar]);
% Limit creation function handle
myLim = @(minV,maxV,border) [minV + border*(maxV - minV), ...
maxV - border*(maxV - minV)];
% 'XLim' and 'YLim' values for process and controller output plots
xLim1 = myLim(minT1, maxT1, handles.params.sqwdx);
yLim1 = myLim(minY , maxY , handles.params.sqwdy);
xLim2 = myLim(minT2, maxT2, handles.params.sqwdx);
yLim2 = myLim(minU , maxU , handles.params.sqwdy);
if moveAxes % axes limits need to be updated
if newPlot && ~fieldExists(handles,'line') % Executes when opening the
% GUI or when running it
% again if already opened
% X-axes line for process and controller output plots
handles.lines.xLine(1) = plot(handles.processOutput, xLim1, ...
[0,0],'k');
handles.lines.xLine(2) = plot(handles.controllerOutput, xLim2, ...
[0,0],'k');
% Y-axes line for process and controller output plots
handles.lines.yLine(2) = plot(handles.controllerOutput, ...
[0,0], yLim2,'k');
handles.lines.yLine(1) = plot(handles.processOutput, [0,0], ...
yLim1,'k');
% Setpoint point and lines for process output plot
handles.lines.setpoint.xLine(1) = plot(handles.processOutput, ...
xLim1, [handles.params.setpoint, handles.params.setpoint],...
'g:');
handles.lines.setpoint.yLine(1) = plot(handles.processOutput, ...
[0,0], [0, handles.params.setpoint],'g-');
handles.lines.setpoint.point(1) = plot(handles.processOutput, ...
0, handles.params.setpoint, 'marker', 'o', 'color', 'g', ...
'MarkerFaceColor','w', 'Tag', 'setpoint1',...
'ButtonDownFcn', {@startDragPoint,handles});
% Setpoint point and lines for controller output plot
handles.lines.setpoint.yLine(2) = plot(handles.controllerOutput,...
[0,0], [0, handles.params.setpoint],'g-');
handles.lines.setpoint.point(2) = plot(handles.controllerOutput,...
0, handles.params.setpoint, 'marker', 'o', 'color', 'g', ...
'MarkerFaceColor', 'w', 'Tag', 'setpoint2', ...
'ButtonDownFcn', {@startDragPoint,handles});
% Load disturbance magnitude, time and line for process output plot
handles.lines.disturbance.yLine(1) = plot(handles.processOutput,...
[handles.params.distTime, handles.params.distTime], ...
[0, handles.params.distMag], 'g-');
handles.lines.disturbance.mag(1) = plot(handles.processOutput, ...
handles.params.distTime, handles.params.distMag, ...
'marker', 'o', 'color', 'g', 'MarkerFaceColor','w', ...
'Tag', 'distMag1', 'ButtonDownFcn', {@startDragPoint,handles});
handles.lines.disturbance.time(1) = plot(handles.processOutput, ...
handles.params.distTime, 0, 'marker', 'o', 'color', 'g', ...
'MarkerFaceColor','w', 'Tag', 'distTime1', ...
'ButtonDownFcn', {@startDragPoint,handles});
% Load disturbance mag., time and line for controller output plot
handles.lines.disturbance.yLine(2) = ...
plot(handles.controllerOutput, ...
[handles.params.distTime, handles.params.distTime], ...
[0, handles.params.distMag],'g-');
handles.lines.disturbance.mag(2) = plot(...
handles.controllerOutput, ...
handles.params.distTime, handles.params.distMag, ...
'marker', 'o', 'color', 'g', 'MarkerFaceColor','w', ...
'Tag', 'distMag2', ...
'ButtonDownFcn', {@startDragPoint,handles});
handles.lines.disturbance.time(2) = plot(...
handles.controllerOutput, ...
handles.params.distTime, 0, 'marker', 'o', 'color', 'g', ...
'MarkerFaceColor','w', 'Tag', 'distTime2', ...
'ButtonDownFcn', {@startDragPoint,handles});
% Noise variance, time and line for process output plot
handles.lines.noise.yLine(1) = plot(handles.processOutput, ...
[handles.params.noiseTime, handles.params.noiseTime], ...
[0, handles.params.noiseVar],'k-');
handles.lines.noise.var(1) = plot(handles.processOutput, ...
handles.params.noiseTime, handles.params.noiseVar, ...
'marker', 'o', 'color', 'k', 'MarkerFaceColor','w', ...
'Tag', 'noiseVar1', ...
'ButtonDownFcn', {@startDragPoint,handles});
handles.lines.noise.time(1) = plot(handles.processOutput, ...
handles.params.noiseTime, 0, 'marker', 'o', 'color', 'k', ...
'MarkerFaceColor','w', 'Tag', 'noiseTime1', ...
'ButtonDownFcn', {@startDragPoint,handles});
% Noise variance, time and line for controller output plot
handles.lines.noise.yLine(2) = plot(handles.controllerOutput, ...
[handles.params.noiseTime, handles.params.noiseTime], ...
[0, handles.params.noiseVar],'k-');
handles.lines.noise.var(2) = plot(handles.controllerOutput, ...
handles.params.noiseTime, handles.params.noiseVar, ...
'marker', 'o', 'color', 'k', 'MarkerFaceColor','w', ...
'Tag', 'noiseVar2', ...
'ButtonDownFcn', {@startDragPoint,handles});
handles.lines.noise.time(2) = plot(handles.controllerOutput, ...
handles.params.noiseTime, 0, 'marker', 'o', 'color', 'k', ...
'MarkerFaceColor','w', 'Tag', 'noiseTime2', ...
'ButtonDownFcn', {@startDragPoint,handles});
% Process and controller output plot lines
handles.lines.line(1) = plot(handles.processOutput, ty, y,...
'tag', 'process','ButtonDownFcn', {@startCursor,handles});
handles.lines.line(2) = plot(handles.controllerOutput, tu, u,...
'tag', 'controller','ButtonDownFcn', {@startCursor,handles});
else % Process and controller output plot and X-Y axes lines update
set(handles.lines.yLine(1), 'YData', yLim1)
set(handles.lines.yLine(2), 'YData', yLim2)
set(handles.lines.line(1) , 'XData', ty, 'YData',y)
set(handles.lines.line(2) , 'XData', tu, 'YData',u)
end
% Axes limits update
set(handles.processOutput, 'Xlim', xLim1, 'Ylim', yLim1)
set(handles.controllerOutput, 'Xlim', xLim2, 'Ylim', yLim2)
else % axes limits don't need to be updated
% Process and controller output plots update
set(handles.lines.line(1), 'XData', ty, 'YData', y)
set(handles.lines.line(2), 'XData', tu, 'YData', u)
yCurLim1 = get(handles.processOutput, 'YLim');
maxY = max(maxY,yCurLim1(2));
yLim1 = myLim(minY, maxY, handles.params.sqwdy);
set(handles.lines.yLine(1), 'YData', yLim1)
set(handles.lines.yLine(2), 'YData', ...
get(handles.controllerOutput,'YLim'))
end
% --- Auxiliary function to check if a fieldName exists in a structure.
function isFieldResult = fieldExists(thisStruct, fieldName)
isFieldResult = 0;
f = fieldnames(thisStruct(1));
for i=1:length(f)
if(strcmp(f{i},strtrim(fieldName)))
isFieldResult = 1;
return;
elseif isstruct(thisStruct(1).(f{i}))
isFieldResult = fieldExists(thisStruct(1).(f{i}), fieldName);
if isFieldResult
return;
end
end
end
% --- Function for performance metrics calculation.
function handles = performanceMetrics(handles)
T = (handles.C*handles.G)/(1 + handles.C*handles.G);
perf = stepinfo(T);
if ~isnan(perf.Overshoot)
perf.OvershootTime = handles.params.performance.overshootTime;
perf.Sigmau = handles.params.performance.sigmau;
else
perf.OvershootTime = NaN;
perf.Sigmau = NaN;
end
hText = {'text_overshoot_num', 'text_overshoottime_num',...
'text_risetime_num', 'text_emax_num', 'text_tmax_num', ...
'text_sigmau_num'};
fields = {'Overshoot','OvershootTime','RiseTime','Peak','PeakTime',...
'Sigmau'};
for i = 1:length(hText)
set(handles.(hText{i}), 'String', sprintf('%0.2f', ...
perf.(fields{i})))
end
set(handles.text_sigmau_num, 'String', sprintf('%0.3f', perf.Sigmau))
% --- Function for robustness metrics calculation.
function handles = robustnessMetrics(handles)
S = 1/(1 + handles.C*handles.G);
T = (handles.C*handles.G)/(1 + handles.C*handles.G);
warning('off','Control:analysis:MarginUnstable')
lastwarn('','');
[Gm,Pm] = margin(handles.C*handles.G);
Ms = norm(S,Inf);
Mt = norm(T,Inf);
hText = {'text_Gm_num','text_Pm_num','text_Ms_num','text_Mt_num'};
data = [Gm, Pm, Ms, Mt];
for i = 1:length(hText)
set(handles.(hText{i}), 'String', sprintf('%0.2f', data(i)))
end
[msg,id] = lastwarn;
% Warning dialog for unstable closed-loop system
if strcmp(id,'Control:analysis:MarginUnstable')
if ~isempty(handles.warn) && ishandle(handles.warn)
set(handles.warn,'Visible','on')
else
handles.warn = warndlg(msg,'Warning','replace');
end
else
if ~isempty(handles.warn) && ishandle(handles.warn)
close(handles.warn);
handles.warn = [];
end
end
warning('on','Control:analysis:MarginUnstable')
% --- Executes when clicking on process or controller output plots.
function startCursor(obj,evnt,handles)
set(handles.PIDBasics, 'WindowButtonMotionFcn', {@cursorMotion,obj})
set(handles.PIDBasics, 'WindowButtonUpFcn' , @stopCursor)
set(handles.PIDBasics, 'Pointer', 'hand')
cursorMotion(handles.PIDBasics,evnt,obj)
% --- Executes when moving over process/controller output plots after click
function cursorMotion(src,evnt,obj)
handles = guidata(src);
plotTag = get(obj, 'tag');
cText = @(x,y) sprintf(' t = %0.2f, y = %0.2f ', x, y);
% Find handle for the plot that has been activated
hPlot = (1 - strcmp(plotTag, 'process')) * handles.controllerOutput + ...
(1 - strcmp(plotTag,'controller')) * handles.processOutput;
point = get(hPlot, 'CurrentPoint');
x = point(1,1);
% Find x and y CurrentPoint locations for process and controller output
% plots
xData1 = get(handles.lines.line(1), 'XData');
yData1 = get(handles.lines.line(1), 'YData');
xData2 = get(handles.lines.line(2), 'XData');
yData2 = get(handles.lines.line(2), 'YData');
y1 = interp1(xData1, yData1, x);
y2 = interp1(xData2, yData2, x);
% Setting cursor (marker) position and text box
set(handles.cursor(1), 'XData', x, 'YData', y1)
set(handles.cursor(2), 'XData', x, 'YData', y2)
set(handles.hText(1), 'Position', [x, y1], 'String', cText(x, y1));
set(handles.hText(2), 'Position', [x, y2], 'String', cText(x, y2));
% --- Executes when releasing click on process or controller output plots.
function stopCursor(src,evnt)
handles = guidata(src);
set(handles.PIDBasics, 'Pointer', 'arrow')
set(handles.hText, 'Position', [NaN,NaN], 'String', '');
set(handles.cursor, 'XData', NaN, 'YData', NaN)
set(handles.PIDBasics, 'WindowButtonMotionFcn', '')
set(handles.PIDBasics, 'WindowButtonUpFcn' , '')
% --- Executes when clicking on one of the created draggable points.
function startDragPoint(obj,evnt,handles)
set(handles.PIDBasics, 'WindowButtonMotionFcn', {@draggingPoint,obj})
set(handles.PIDBasics, 'WindowButtonUpFcn' , {@stopDragPoint,obj})
set(handles.PIDBasics, 'Pointer', 'hand')
set(obj,'MarkerFaceColor',get(obj,'color'))
% --- Executes when moving one of the created draggable points.
function draggingPoint(src,evnt,obj)
handles = guidata(src);
pointTag = get(obj, 'tag');
switch pointTag % Find which point is has been clicked and update plots
% accordingly
case {'setpoint1','setpoint2'}
point = selectedPoint(handles, pointTag, ...
{'setpoint1','setpoint2'});
if point(1,2) >= 0
set(handles.lines.setpoint.xLine(1), 'YData', point(1,2)*[1,1])
set(handles.lines.setpoint.yLine , 'YData', point(1,2)*[0,1])
set(handles.lines.setpoint.point , 'YData', point(1,2))
handles.params.setpoint = point(1,2);
handles = processAndControllerPlots(handles, false, false);
end
case {'distMag1','distMag2'}
point = selectedPoint(handles, pointTag, {'distMag1','distMag2'});
if point(1,2) >= 0
set(handles.lines.disturbance.yLine, 'YData', point(1,2)*[0,1])
set(handles.lines.disturbance.mag , 'YData', point(1,2))
handles.params.distMag = point(1,2);
handles = processAndControllerPlots(handles, false, false);
end
case {'distTime1','distTime2'}
point = selectedPoint(handles, pointTag, ...
{'distTime1','distTime2'});
if point(1,1) >= 0 && point(1,1) <= handles.params.Tfinal
set(handles.lines.disturbance.yLine, 'XData',...
point(1,1)*[1,1])
set(handles.lines.disturbance.time, 'XData', point(1,1))
set(handles.lines.disturbance.mag , 'XData', point(1,1))
handles.params.distTime = point(1,1);
handles = processAndControllerPlots(handles, false, false);
end
case {'noiseVar1','noiseVar2'}
point = selectedPoint(handles, pointTag, ...
{'noiseVar1','noiseVar2'});
if point(1,2) >= 0
set(handles.lines.noise.yLine, 'YData', point(1,2)*[0,1])
set(handles.lines.noise.var , 'YData', point(1,2))
handles.params.noiseVar = point(1,2);
handles = processAndControllerPlots(handles, false, false);
end
case {'noiseTime1','noiseTime2'}
point = selectedPoint(handles, pointTag, ...
{'noiseTime1','noiseTime2'});
if point(1,1) >= 0 && point(1,1) <= handles.params.Tfinal
set(handles.lines.noise.yLine, 'XData', point(1,1)*[1,1])
set(handles.lines.noise.time , 'XData', point(1,1))
set(handles.lines.noise.var , 'XData', point(1,1))
handles.params.noiseTime = point(1,1);
handles = processAndControllerPlots(handles, false, false);
end
end
guidata(src,handles)
% --- Auxiliary function for draggingPoint
function point = selectedPoint(handles, pointTag, tags)
i = find(strcmp(pointTag, tags));
if i == 1
point = get(handles.processOutput, 'CurrentPoint');
else
point = get(handles.controllerOutput, 'CurrentPoint');
end
% --- Executes when releasing click on draggable point.
function stopDragPoint(src,evnt,obj)
handles = guidata(src);
set(obj,'MarkerFaceColor','w')
set(handles.PIDBasics, 'Pointer', 'arrow')
set(handles.PIDBasics, 'WindowButtonMotionFcn', '')
set(handles.PIDBasics, 'WindowButtonUpFcn' , '')
pause(1); % 1-sec pause to notice what has been updated before axes
% relocation
y1 = get(handles.lines.line(1), 'YData');
y2 = get(handles.lines.line(2), 'YData');
minY1 = min(y1); minY2 = min([y2,0]);
maxY1 = max([y1,handles.params.setpoint,handles.params.distMag,...
handles.params.noiseVar]);
maxY2 = max([y2,handles.params.setpoint,handles.params.distMag,...
handles.params.noiseVar]);
myLim = @(minV,maxV,border) [minV + border*(maxV - minV), ...
maxV - border*(maxV - minV)];
yLim1 = myLim(minY1, maxY1, handles.params.sqwdy);
yLim2 = myLim(minY2, maxY2, handles.params.sqwdy);
% YLim gets updated to the new calculated values
set(handles.processOutput , 'YLim', yLim1)
set(handles.controllerOutput, 'YLim', yLim2)
set(handles.lines.yLine(1), 'YData', yLim1)
set(handles.lines.yLine(2), 'YData', yLim2)
% Function that displays the current process formula in the GUI.
function tftext = displayTfProcess(G)
w = whos;
tftext = evalc(w.name);
removePos = regexp(tftext,'[=CS]');
tftext = tftext(removePos(1)+4:removePos(2)-4);
assignTo = 'G(s) = ';
cReturn = double(tftext) == 10;
if nnz(cReturn) == 2 % There is a fraction
cReturns = find(cReturn);
tftext = [char(32*ones(1,length(assignTo))), ...
tftext(3:cReturns(1)), assignTo, ...
tftext(cReturns(1)+3:cReturns(2)), ...
char(32*ones(1,length(assignTo))),tftext(cReturns(2)+3:end)];
else
tftext = [char(10), assignTo, tftext];
end
% --- Computes DC gain by factoring out z and p absolute values.
function Kp = computeDcGain(G)
p = G.p{1};
z = G.z{1};
Kp = G.k*abs(prod(z(z~=0))/prod(p(p~=0)));
% --- Reverse function of computeDcGain.
function Gk = computeDcGainRev(Kp,G)
p = G.p{1};
z = G.z{1};
Gk = Kp*abs(prod(p(p~=0))/prod(z(z~=0)));
% --- Executes on button press in discoveryButton.
function discoveryButton_Callback(hObject, eventdata, handles)
% hObject handle to discoveryButton (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Shows Discovery PID Control website on default browser
web('http://www.mathworks.com/discovery/pid-control.html' ,'-browser')
% --- Executes on button press in customTfProcess.
function customTfProcess_Callback(hObject, eventdata, handles)
% hObject handle to customTfProcess (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
handles.G = tfProcess;
if ~isempty(handles.G)
tfprocessText = displayTfProcess(handles.G);
set(handles.text_tf,'String',tfprocessText)
Kp = computeDcGain(handles.G);
set(handles.text_Kp, 'String', sprintf('Kp: %0.2f', Kp))
set(handles.slider_Kp, 'Min', log10(Kp/4), 'Max', log10(Kp*4), ...
'Value', log10(Kp))
handles = processAndControllerPlots(handles, false, true);
handles = robustnessMetrics(handles);
handles = performanceMetrics(handles);
guidata(hObject, handles)
end
% --- Executes on slider_Kp continuous movement.
function slider_Kp_Scroll(hObject, eventdata, handles)
handles=guidata(hObject);
Kp = power(10,get(hObject,'Value'));
set(handles.text_Kp, 'String', sprintf('Kp: %0.2f', Kp))
handles.G.k = computeDcGainRev(Kp,handles.G);
tfprocessText = displayTfProcess(handles.G);
set(handles.text_tf,'String',tfprocessText)
handles = processAndControllerPlots(handles, false, true);
handles = robustnessMetrics(handles);
handles = performanceMetrics(handles);
guidata(hObject, handles);
% --- Executes on slider_Kp slider movement.
function slider_Kp_Callback(hObject, eventdata, handles)
% hObject handle to slider_Kp (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
Kp = power(10,get(hObject,'Value'));
set(hObject, 'Min', log10(Kp/4), 'Max', log10(Kp*4))
% --- Executes during object creation, after setting all properties.
function slider_Kp_CreateFcn(hObject, eventdata, handles)
% hObject handle to slider_Kp (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles empty - handles not created until after all CreateFcns called
% Hint: slider controls usually have a light gray background.
if isequal(get(hObject,'BackgroundColor'), ...
get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor',[.9 .9 .9]);
end
% --- Executes on slider_(K,Ti,Td,N,b) continuous movement.
function slider_Gains_Scroll(hObject, eventdata, hText)
handles = guidata(hObject);
Gain = power(10,get(hObject,'Value'));
str = textStr(hText);
set(hText, 'String', sprintf([str, '%0.2f'], Gain))
handles = controllerUpdate(handles);
handles = processAndControllerPlots(handles, false, true);
handles = robustnessMetrics(handles);
handles = performanceMetrics(handles);
guidata(hObject, handles);
% --- Executes when choosing a radio button in the Button group
function controller_Change(hObject, eventdata, handles)
handles = guidata(hObject);
selectedRB = get(get(hObject,'SelectedObject'),'String');
switch selectedRB
case{'P'}
enableDisableSlider({'on','off','off','off'}, handles)
case{'I'}
enableDisableSlider({'off','on','off','off'}, handles)
case{'PI'}
enableDisableSlider({'on','on','off','off'}, handles)
case{'PD'}
enableDisableSlider({'on','off','on','on'}, handles)
case{'PID'}
enableDisableSlider({'on','on','on','on'}, handles)
end
handles = controllerUpdate(handles);
handles = processAndControllerPlots(handles, false, true);
handles = robustnessMetrics(handles);
handles = performanceMetrics(handles);
guidata(hObject, handles)
% --- Executes during call to controller_Change, to enable/disable sliders
function enableDisableSlider(enableDisable, handles)
set(handles.slider_K, 'Enable', enableDisable{1})
set(handles.slider_Ti, 'Enable', enableDisable{2})
set(handles.slider_Td, 'Enable', enableDisable{3})
set(handles.slider_N, 'Enable', enableDisable{4})
set(handles.text_K, 'Enable', enableDisable{1})
set(handles.text_Ti, 'Enable', enableDisable{2})
set(handles.text_Td, 'Enable', enableDisable{3})
set(handles.text_N, 'Enable', enableDisable{4})
% --- Executes on slider_K movement.
function slider_K_Callback(hObject, eventdata, handles)
% hObject handle to slider_K (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
K = power(10,get(hObject,'Value'));
set(hObject, 'Min', log10(K/4), 'Max', log10(K*4))
% --- Executes during object creation, after setting all properties.
function slider_K_CreateFcn(hObject, eventdata, handles)
% hObject handle to slider_K (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles empty - handles not created until after all CreateFcns called
% Hint: slider controls usually have a light gray background.
if isequal(get(hObject,'BackgroundColor'), ...
get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor',[.9 .9 .9]);
end
% --- Executes on slider_Ti movement.
function slider_Ti_Callback(hObject, eventdata, handles)
% hObject handle to slider_Ti (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
Ti = power(10,get(hObject,'Value'));
set(hObject, 'Min', log10(Ti/4), 'Max', log10(Ti*4))
% --- Executes during object creation, after setting all properties.
function slider_Ti_CreateFcn(hObject, eventdata, handles)
% hObject handle to slider_Ti (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles empty - handles not created until after all CreateFcns called
% Hint: slider controls usually have a light gray background.
if isequal(get(hObject,'BackgroundColor'), ...
get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor',[.9 .9 .9]);
end
% --- Executes on slider_Td movement.
function slider_Td_Callback(hObject, eventdata, handles)
% hObject handle to slider_Td (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
Td = power(10,get(hObject,'Value'));
set(hObject, 'Min', log10(Td/4), 'Max', log10(Td*4))
% --- Executes during object creation, after setting all properties.
function slider_Td_CreateFcn(hObject, eventdata, handles)
% hObject handle to slider_Td (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles empty - handles not created until after all CreateFcns called
% Hint: slider controls usually have a light gray background.
if isequal(get(hObject,'BackgroundColor'), ...
get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor',[.9 .9 .9]);
end
% --- Executes on slider_N movement.
function slider_N_Callback(hObject, eventdata, handles)
% hObject handle to slider_N (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Hints: get(hObject,'Value') returns position of slider
% get(hObject,'Min') and get(hObject,'Max') to determine range of slider
N = power(10,get(hObject,'Value'));
set(hObject, 'Min', log10(N/4), 'Max', log10(N*4))
% --- Executes during object creation, after setting all properties.
function slider_N_CreateFcn(hObject, eventdata, handles)
% hObject handle to slider_N (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles empty - handles not created until after all CreateFcns called
% Hint: slider controls usually have a light gray background.
if isequal(get(hObject,'BackgroundColor'), ...
get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor',[.9 .9 .9]);
end