% MatLab template file for MAE171A/MAE175A to help you % (1) simulate open loop (uncontrolled) behavior of model % (2) design controller (internal PID or via rltool) and simulate % closed-loop behavior of model and controller % Notes: % - Edit model parameters in the (default) parameter file PARAMETERS.M % - When using rltool for control design, export the controller to % workspace using the variable name C % Written by R.A. de Callafon, Dept. of MAE, UCSD (2004) clc drawnow; format compact % EDIT THE PARAMETERS OF YOUR MODEL IN THE DEFAULT FILE PARAMETERS.M % Ask for the filename with the parameters: disp('- Welcome to MAELAB') cantfindit=1; while cantfindit, cantfindit=0; typo=1; while typo==1, typo=0; if exist('parfile')==1, parfilep=input(['Name of file with model parameters [''' parfile ''']: ']); if ~isempty(parfilep), parfile=parfilep; end else parfile=input('Name of file with model parameters [''parameters'']: '); if isempty(parfile), parfile='parameters'; end end if abs(parfile(1))<65, typo=1; clear parfilep parfile disp(char(7)) disp(['==> Sorry, incorrect filename, choosing default name ''parameters''']); end end if exist('parfile')==1, lfname=length(parfile); if parfile(lfname-1)~='.', parfile=[parfile '.m']; end eval(['testje=exist(''' parfile ''');']); if testje~=2, disp(['==> Sorry, cannot find ' parfile ]); cantfindit=1; end lfname=length(parfile); parfile=parfile(1:lfname-2); end end % Evoke parameter file disp(['Evoking ' parfile '.m to update your model parameters']); eval(parfile); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % The parameters and the transfer function given below is based on the following model: % We assume a 2DOF system of two masses/inertias m1 and m2. The two masses/inertia % are connected via a spring k2 while m1 is connected via a spring k1 to the ground. % Each of the masses/inertia are connected to the ground via a damping of respectively % d1 and d2. A force/torque is applied to mass m1 only. % The position/rotation of the mass/inertia m1 is denoted by x1 % The position/rotation of the mass/inertia m2 is denoted by x2 % The voltage applied to the electromotor is denoted by u and is proportional % to the generated force/torque. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % The model below is a transfer function from control effort u (measured in voltage) % % to the position/rotation x1 or x2 of mass/inertia m1 or m2 (measured in counts). % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% typo=1; while typo==1, if exist('encoderout')==1, encoderoutep=input(['Specify encoder output (1/2) [' num2str(encoderout) ']: ']); if ~isempty(encoderoutep), encoderout=encoderoutep; end else encoderout=input(['Specify encoder output (1/2): ']); while isempty(encoderout), clear encoderout encoderout=input(['Specify encoder output (1/2): ']); end end if ~((encoderout==1)|(encoderout==2)), clear encoderout else typo=0; end end % model is given by a numerator `numg' and denominator `deng' % and are specified here: numg1=[m2 d2 k2]; % for x1 as output numg2=k2; % for x2 as output deng=[m1*m2 (m1*d2+m2*d1) (k2*m1+(k1+k2)*m2+d1*d2) ((k1+k2)*d2+k2*d1) k1*k2]; if encoderout==1, numg=numg1; % we're using x1 as output else numg=numg2; % we're using x2 as output end clear numg1 numg2 % to include dynamics of the DC-motor (actuator), the total denominator becomes deng0=deng; deng=conv(deng,[1/209 1]); % to include the hardware gain, the total numerator becomes numg0=numg; Khw=9.7656e-3; % hardware gain, only plays a role in controller implementation numg=Khw*numg; % For Matlab 5.3 and higher in case students like to use root locus method % for control design: G=tf(numg,deng); % includes dynamics of mechanics + motor + hardware gain % In order to simulate Bode plots, % a frequency vector is specified here: f=logspace(-2,2,500); % in Herz, logarithmically spaced % creating figures for figs=1:6, figure(figs); set(figs,'Visible','off'); end % % Now we enter the menu. % Please search throughout the template file for the different % options to see what is going on % disp(' '); disp('- Choose an option:'); disp(' '); disp('(1) Bode plot of G(s) '); disp('(2) Simulate/compare open loop step response '); disp('(3) Simulate/compare open loop impulse response'); disp('(4) Design/evaluate feedback controller'); disp('(5) Simulate closed loop response'); disp('(6) Exit this menu'); option=0; while option==0, option=input('Enter your choice: '); if isempty(option), option=0; end option=round(max(option)); if option>6, option=0; end if option<1, option=0; end end if option==1, disp('- Mechanical resonance modes of the model G(s):'); damp(deng0); disp('Note: Model G(s) also contains motor dynamics and is available') disp(' in the Matlab workspace via the variable G') % % Here we compute frequency response % [mag,pha]=bode(numg,deng,2*pi*f); % % Here we plot the Bode response response % figure(1) plttitle='Bode plot of G(s)'; set(1,'Userdata',plttitle'); set(1,'Visible','on'); clf % use to be clg subplot(2,1,1); loglog(f,mag); title(plttitle) xlabel('frequency [Hz]'); ylabel('magnitude') grid subplot(2,1,2); semilogx(f,pha); xlabel('frequency [Hz]'); ylabel('phase [deg]'); grid disp(['- Figure 1: ' plttitle]) end if option==2, if exist('stepsize')==1, stepsizep=input(['Enter (open loop) step size [' num2str(stepsize) ' Volts]: ']); if ~isempty(stepsizep), stepsize=stepsizep; end else stepsize=input('Enter (open loop) step size [Volts]: '); while isempty(stepsize), clear stepsize stepsize=input('Enter (open loop) step size [Volts]: '); end end if exist('dwelltime')==1, dwelltimep=input(['Enter dwell time [' num2str(dwelltime) ' msec]: ']); if ~isempty(dwelltimep), dwelltime=dwelltimep; end else dwelltime=input('Enter dwell time [msec]: '); while isempty(dwelltime), clear dwelltime dwelltime=input('Enter dwell time [msec]: '); end end stepsize=abs(stepsize(1)); dwelltime=abs(dwelltime(1)); typo=1; while typo==1, typo=0; if exist('sfilename')~=1, sfilename=input('File that contains step experiment [ENTER for none]: '); else osfilename=sfilename; sfilename=input(['File that contains step experiment [ENTER for ''' sfilename ''' or 0 for none]: ']); if length(sfilename)==0, sfilename=osfilename; end end if length(sfilename)~=0, if abs(sfilename(1))==0, clear sfilename osfilename elseif abs(sfilename(1))<65 typo=1; if exist('osfilename')==1, sfilename=osfilename; else clear sfilename end end else clear sfilename end if exist('sfilename')==1, lfname=length(sfilename); if sfilename(lfname-1)~='.', sfilename=[sfilename '.m']; end eval(['testje=exist(''' sfilename ''');']); if testje~=2, disp(['==> Sorry, cannot find ' sfilename ]); typo=1; clear sfilename osfilename else lfname=length(sfilename); sfilename=sfilename(1:lfname-2); end end end if exist('sfilename')==1, lfname=length(sfilename); if sfilename(lfname-1)~='.', sfilename=[sfilename '.m']; end disp(['- Evoking ' sfilename ' to define t and y from step experiment']); lfname=length(sfilename); sfilename=sfilename(1:lfname-2); clear t y eval(sfilename); if (exist('y')~=1)|(exist('t')~=1), disp('==> Sorry, incorrect file format: no output y or time vector t defined'); disp(' Skipping comparison!') t=[];y=[]; end else t=[];y=[]; end disp('- Computing step response of G(s)'); % % Here we compute step response % tstep=linspace(0,2*1e-3*dwelltime,900)'; ustep=[stepsize*ones(450,1);0*ones(450,1)]; % the step response without the hardware gain Khw that only plays a role in % feedback controller implementation ystep=lsim(numg0,deng,ustep,tstep); % % Here we plot the time response % figure(2) if length(y)>0, plttitle='Simulated (solid) and measured (dashed) open loop step response'; else plttitle='Simulated open loop step response'; end set(2,'Userdata',plttitle'); set(2,'Visible','on'); clf % use to be clg plot(tstep,ystep,t,y,':'); title(plttitle); xlabel('time [sec]'); ylabel('output signals (solid, dashed)') disp(['- Figure 2: ' plttitle]); end if option==3, if exist('imptime')==1, imptimep=input(['Enter length of impulse response [' num2str(imptime) ' msec]: ']); if ~isempty(imptimep), imptime=imptimep; end else imptime=input('Enter length of impulse response [msec]: '); while isempty(imptime), clear imptime imptime=input('Enter length of impulse response [msec]: '); end end if exist('deltime')==1, deltimep=input(['Enter delay of impulse excitation [' num2str(deltime) ' msec]: ']); if ~isempty(deltimep), deltime=deltimep; end else deltime=input('Enter delay of impulse excitation [msec]: '); while isempty(deltime), clear deltime deltime=input('Enter delay of impulse excitation [msec]: '); end end if exist('impscale')==1, impscalep=input(['Enter scaling of impulse response [' num2str(impscale) ']: ']); if ~isempty(impscalep), impscale=impscalep; end else impscale=input('Enter scaling of impulse response [1]: '); if isempty(impscale), impscale=1; end end imptime=abs(imptime(1)); deltime=abs(deltime(1)); if deltime>imptime, deltime=imptime; end typo=1; while typo==1, typo=0; if exist('ifilename')~=1, ifilename=input('File that contains impulse experiment [ENTER for none]: '); else oifilename=ifilename; ifilename=input(['File that contains impulse experiment [ENTER for ''' ifilename ''' or 0 for none]: ']); if length(ifilename)==0, ifilename=oifilename; end end if length(ifilename)~=0, if abs(ifilename(1))==0, clear ifilename oifilename elseif abs(ifilename(1))<65 typo=1; if exist('oifilename')==1, ifilename=oifilename; else clear ifilename end end else clear ifilename end if exist('ifilename')==1, lfname=length(ifilename); if ifilename(lfname-1)~='.', ifilename=[ifilename '.m']; end eval(['testje=exist(''' ifilename ''');']); if testje~=2, disp(['==> Sorry, cannot find ' ifilename ]); typo=1; clear ifilename oifilename else lfname=length(ifilename); ifilename=ifilename(1:lfname-2); end end end if exist('ifilename')==1, lfname=length(ifilename); if ifilename(lfname-1)~='.', ifilename=[ifilename '.m']; end disp(['- Evoking ' ifilename ' to define t and y from impulse experiment']); lfname=length(ifilename); ifilename=ifilename(1:lfname-2); clear t y eval(ifilename); if (exist('y')~=1)|(exist('t')~=1), disp('==> Sorry, incorrect file format: no output y or time vector t defined'); disp(' Skipping comparison!') t=[];y=[]; end else t=[];y=[]; end disp('- Computing (scaled) impulse response of G(s)'); % % Here we compute impulse response % timp=linspace(0,1e-3*imptime,900)'; % the impulse response without the hardware gain Khw that only plays a role in % feedback controller implementation yimp=impulse(numg0,deng,timp)*impscale; index=find(timp<=deltime*1e-3); yimp=[zeros(length(index),1);yimp(1:length(yimp)-length(index))]; % % Here we plot the time response % figure(3) if length(y)>0, plttitle='Simulated (solid) and measured (dashed) impulse response'; else plttitle='Simulated impulse response'; end set(3,'Userdata',plttitle'); set(3,'Visible','on'); clf % use to be clg plot(timp,yimp,t,y,':'); title(plttitle); xlabel('time [sec]'); ylabel('output signals (solid, dashed)') disp(['- Figure 3: ' plttitle]); end if option==4, usethiscontrol=0; if exist('C')==1, usethiscontrol=2; while ((usethiscontrol~=0)&(usethiscontrol~=1)), usethiscontrol=input('Controller C found in memory: use this controller? [1/0]: '); if isempty(usethiscontrol), usethiscontrol=2; end end end % Note: tau is needed and introduced to make controller realizable % for the differentiating part tau=2*pi*0.0001; if usethiscontrol==0, disp('- Enter the control design variables'); if exist('kp')==1, kpp=input(['Give proportional gain kp [' num2str(kp) ']: ']); if ~isempty(kpp), kp=kpp; end else kp=input('Give proportional gain kp: '); while isempty(kp), clear kp kp=input('Give proportional gain kp: '); end end if exist('kd')==1, kdp=input(['Give proportional gain kd [' num2str(kd) ']: ']); if ~isempty(kdp), kd=kdp; end else kd=input('Give proportional gain kd: '); while isempty(kd), clear kd kd=input('Give proportional gain kd: '); end end if exist('ki')==1, kip=input(['Give proportional gain ki [' num2str(ki) ']: ']); if ~isempty(kip), ki=kip; end else ki=input('Give proportional gain ki: '); while isempty(ki), clear ki ki=input('Give proportional gain ki: '); end end kp=abs(kp(1)); kd=abs(kd(1)); ki=abs(ki(1)); % General form of controller. numc=[kd kp ki]; denc=[tau 1 0]; % Clearly, if ki = 0, we can remove the pole and zero at 0 from the % the general form of the controller, since we have a PD controller if ki==0, numc=[kd kp]; denc=[tau 1]; end % For matlab 5.3 and higher Cpid=tf(numc,denc); else % get the information from the controller in workspace [numc,denc]=tfdata(C); numc=numc{1}; denc=denc{1}; % add the extra time constant to the controller denc=conv(denc,[tau 1]); % For matlab 5.3 and higher (update the controller) Cpid=tf(numc,denc); end disp('- Controller transfer function (including motor dynamics):'); Cpid disp('- Computing controller and closed loop Bode plots'); % computing Bode plot of controller [magc,phac]=bode(numc,denc,2*pi*f); % computing loop gain [numl,denl]=series(numg,deng,numc,denc); % computing Bode plot of loop gain [magl,phal]=bode(numl,denl,2*pi*f); [rel,iml]=nyquist(numl,denl,2*pi*f); % computing closed loop output connection (using negative feedback connection) [numcl,dencl]=cloop(numl,denl,-1); % computing closed loop input connection (using negative feedback connection) dens=dencl; nums=conv(numc,deng); % computing Bode plot of closed loop transfer function [magcl,phacl]=bode(numcl,dencl,2*pi*f); % check stability stability=1; clpoles=roots(dencl); if max(real(clpoles))>0, stability=0; disp('==> CLOSED LOOP UNSTABLE'); end % % Here we plot the Bode plot of the controller % figure(4) plttitle='Bode plot of controller C(s)'; set(4,'Userdata',plttitle'); set(4,'Visible','on'); clf % use to be clg subplot(2,1,1); loglog(f,magc); title(plttitle) xlabel('frequency [Hz]'); ylabel('magnitude') subplot(2,1,2); semilogx(f,phac); xlabel('frequency [Hz]'); ylabel('phase [deg]'); disp(['- Figure 4: ' plttitle]) % % Here we plot the Nyquist plot and closed-loop transfer function % figure(5) plttitle='Frequency plots of loopgain L(s)'; set(5,'Userdata',plttitle'); set(5,'Visible','on'); clf % use to be clg subplot(2,2,1); loglog(f,magl); hold on loglog([f(1) f(length(f))],[1 1],':') hold off title('magnitude L(s)') xlabel('frequency [Hz]'); ylabel('magnitude') axiss=axis; axis([f(1) f(length(f)) axiss(3) axiss(4)]); subplot(2,2,2); plot(rel,iml); axis([-4 4 -4 4]); hold on plot(-1,0,'*') hold off title('Nyquist L(s)') xlabel('real') ylabel('imag') subplot(2,2,3); semilogx(f,phal); hold on semilogx([f(1) f(length(f))],[-180 -180],':'); hold off xlabel('frequency [Hz]'); ylabel('phase [deg]'); title('Phase L(s)') axiss=axis; axis([f(1) f(length(f)) axiss(3) axiss(4)]); subplot(2,2,4); loglog(f,magcl); hold on loglog([f(1) f(length(f))],[1 1],':') hold off title('magnitude L(s)/(1+L(s))') xlabel('frequency [Hz]'); ylabel('magnitude') axiss=axis; axis([f(1) f(length(f)) axiss(3) axiss(4)]); if stability==1, text(0.1,0.1,'closed loop stable','color','green','Units','normalized'); else text(0.1,0.1,'closed loop unstable!','color','red','Units','normalized'); end disp(['- Figure 5: ' plttitle]) end if option==5, if exist('stability')==0, disp('==> First run option 4 to evaluate the controller '); else disp('- Checking closed-loop stability fo previously evaluated controller'); % recheck stability in case you changed the encoder output... % computing loop gain [numc,denc]=tfdata(Cpid); numc=numc{1};denc=denc{1}; [numl,denl]=series(numg,deng,numc,denc); % computing closed loop output connection (using negative feedback connection) [numcl,dencl]=cloop(numl,denl,-1); % check stability stability=1; clpoles=roots(dencl); if max(real(clpoles))>0, stability=0; end if stability==1, if exist('clstepsize')==1, clstepsizep=input(['Enter (closed loop) step size [' num2str(clstepsize) ' counts]: ']); if ~isempty(clstepsizep), clstepsize=clstepsizep; end else clstepsize=input('Enter (closed loop) step size [counts]: '); while isempty(clstepsize), clear clstepsize clstepsize=input('Enter (closed loop) step size [counts]: '); end end if exist('cldwelltime')==1, cldwelltimep=input(['Enter dwell time [' num2str(cldwelltime) ' msec]: ']); if ~isempty(cldwelltimep), cldwelltime=cldwelltimep; end else cldwelltime=input('Enter dwell time [msec]: '); while isempty(cldwelltime), clear cldwelltime cldwelltime=input('Enter dwell time [msec]: '); end end clstepsize=abs(clstepsize(1)); cldwelltime=abs(cldwelltime(1)); cltstep=linspace(0,2*1e-3*cldwelltime,900)'; clustep=[clstepsize*ones(450,1);0*ones(450,1)]; % closed loop step response of output ystepcl=lsim(numcl,dencl,clustep,cltstep); % closed loop step response of input ustepcl=Khw*lsim(nums,dens,clustep,cltstep); figure(6) plttitle='Closed loop simulated step response'; set(6,'Userdata',plttitle'); set(6,'Visible','on'); clf % use to be clg subplot(2,1,1); plot(cltstep,clustep,'--',cltstep,ystepcl,'-'); title(plttitle); xlabel('time [sec]'); ylabel('reference (dashed), output (solid)') subplot(2,1,2); plot(cltstep,ustepcl); xlabel('time [sec]'); ylabel('control signal [V]') %axis([cltstep(1) cltstep(length(cltstep)) -5 5]); disp(['- Figure 6: ' plttitle]) else disp('==> CLOSED LOOP SYSTEM UNSTABLE'); disp([' Redesign controller around encoder ' num2str(encoderout) ' with option 4']); set(6,'Visible','off') end end end if option==6, disp('- Exciting MAELAB...'); end clear typo cantfindit deltatimep deltimep imptimep index lfname figs numg deng f clear option parfilep testje mag pha plttitle dwelltimep stepsizep clear axiss numc denc numl denl clear rel iml magc magcl magl phac phacl phal tau clear kpp kdp kip