CMU
UM


Function nyquist1:
plotting Nyquist frequency response for continuous-time linear systems

Below is the function nyquist1.m. This function is a modified version of the nyquist command, and has all the same attributes as the original, with a few improvements. Nyquist1.m takes poles on the imaginary axis into account when creating the Nyquist plot, and plots around them. Furthermore, this function outputs the number of open-loop right hand plane poles, the number of anti-clockwise encirclements, and the number of closed-loop right half plane poles onto your screen.

Copy the following text into a file nyquist.m, and put it in the same directory as the Matlab software, or in a directory which is contained in Matlab's search path.


function [reout,imt,w] = mnyquist(a,b,c,d,iu,w)
%NYQUIST1 Nyquist frequency response for continuous-time linear systems.
%
%	This Verison of the  NYQUIST Command takes into account poles at the 
%	jw-axis and loops around them when creating the frequency vector  in order 
%	to produce the appropriate Nyquist Diagram (The NYQUIST command does 
%	not do this and therefore produces an incorrect plot when we have poles in the 
%	jw axis).   As an added feature, this function outputs the number of open loop right
%	 hand plane poles, the number of anti-clockwise encirclements, and the number of 
%	closed loop right half plane poles on your screen. 
%
%	NOTE: This version of NYQUIST1 does not account for pole-zero 
%	cancellation.  Therefore, the user must simplify the transfer function before using 
%	this command.
%
%	NYQUIST1(A,B,C,D,IU) produces a Nyquist plot from the single input
%	IU to all the outputs of the system:             
%               .                                    -1
%               x = Ax + Bu             G(s) = C(sI-A) B + D  
%               y = Cx + Du      RE(w) = real(G(jw)), IM(w) = imag(G(jw))
%
%	The frequency range and number of points are chosen automatically.
%
%	NYQUIST1(NUM,DEN) produces the Nyquist plot for the polynomial 
%	transfer function G(s) = NUM(s)/DEN(s) where NUM and DEN contain
%	the polynomial coefficients in descending powers of s. 
%
%	NYQUIST1(A,B,C,D,IU,W) or NYQUIST1(NUM,DEN,W) uses the user-supplied
%	freq. vector W which must contain the frequencies, in radians/sec,
%	at which the Nyquist response is to be evaluated.  When invoked 
%	with left hand arguments,
%		[RE,IM,W] = NYQUIST1(A,B,C,D,...)
%		[RE,IM,W] = NYQUIST1(NUM,DEN,...) 
%	returns the frequency vector W and matrices RE and IM with as many
%	columns as outputs and length(W) rows.  No plot is drawn on the 
%	screen.
%
%	Modified by Mithran Mathew at Carnegie Mellon University 
%	under the supervision of Dr. William Messner.
%	
%
%	See also: LOGSPACE,MARGIN,BODE, and NICHOLS.

% 	J.N. Little 10-11-85
%	Revised ACWG 8-15-89, CMT 7-9-90, ACWG 2-12-91, 6-21-92, 
%               AFP 2-23-93
%	Copyright (c) 1986-93 by the MathWorks, Inc.
%

%********************************************************************
%  Modifications made to the nyquist - takes into account poles on jw axis.
%  then goes around these to make up frequency vector
  
if nargin==0, eval('exresp(''nyquist'')'), return, end

% --- Determine which syntax is being used ---
if (nargin==1),
	error('Wrong number of input arguments.');

elseif (nargin==2),	% Transfer function form without frequency vector
	num  = a; den = b; 
	w = freqint2(num,den,30);
	[ny,nn] = size(num); nu = 1;

elseif (nargin==3),	% Transfer function form with frequency vector
	num = a; den = b;
	w = c;
	[ny,nn] = size(num); nu = 1;

elseif (nargin==4),	% State space system, w/o iu or frequency vector
	error(abcdchk(a,b,c,d));
	w = freqint2(a,b,c,d,30);
	[iu,nargin,re,im]=mulresp('nyquist',a,b,c,d,w,nargout,0);
	if ~iu, if nargout, reout = re; end, return, end
	[ny,nu] = size(d);

elseif (nargin==5),	% State space system, with iu but w/o freq. vector
	error(abcdchk(a,b,c,d));
	w = freqint2(a,b,c,d,30);
	[ny,nu] = size(d);

else
	error(abcdchk(a,b,c,d));
	[ny,nu] = size(d);

end

if nu*ny==0, im=[]; w=[]; if nargout~=0, reout=[]; end, return, end

% ********************************************************************* 
% depart from the regular nyquist program here
% now we have a frequency vector, a numerator and denominator
% now we create code to go around all poles and zeroes here.

if (nargin==5) | (nargin ==4) | (nargin == 6)
	[num,den]=ss2tf(a,b,c,d)
end 

tol = 1e-6;         %defined tolerance for finding imaginary poles

z = roots(num);
p = roots(den);
zp = [z;p];         % combine the zeros and poles of the system
nzp = length(zp);  % number of zeros and poles
ones_zp=ones(nzp,1); 

%index poles with zero real part + non-neg imag part
Ipo = find((abs(real(p))=0)); 

% **** only if we have such poles do we do the following:****

if  ~isempty(Ipo)    
	po = p(Ipo); % poles with 0 real part and non-negative imag part
	% check for distinct poles
	[y,ipo] = sort(imag(po));  % sort imaginary parts
	po = po(ipo);
	dpo = diff(po);
	idpo = find(abs(dpo)>tol);
	idpo = [1;idpo+1];   % indexes of the distinct poles
	po = po(idpo);   % only distinct poles are used 
	nIpo = length(idpo); % # of such poles
	originflag = find(imag(po)==0);  % locate origin pole

	s = [];  % s is our frequency response vector
	w = sqrt(-1)*w;  % create a jw vector to evaluate all frequencies with
	
	for ii=1:nIpo % for all Ipo poles
		[nrows,ncolumns]=size(w);
		if nrows == 1
			w = w.';  % if w is a row, make it a column
		end;

		if nIpo == 1
			r(ii) = .1;
		
		else  	% check distances to other poles and zeroes
			pdiff = zp-po(ii)*ones_zp  % find the differences between
			% poles you are checking and other poles and zeros
			ipdiff = find(abs(pdiff)>tol) % ipdiff is all nonzero differences
		
			r(ii)=0.2*min(abs(pdiff(ipdiff))) % take half this difference
			r(ii)=min(r(ii),0.1)  % take the minimum of this diff.and .1
		end;
	
		t = linspace(-pi/2,pi/2,25); 
	
		if (ii == originflag)
			t = linspace(0,pi/2,25);
		end;
	
		% gives us a vector of points around each Ipo  
		s1 = po(ii)+r(ii)*(cos(t)+sqrt(-1)*sin(t));  % detour here
		s1 = s1.';  % make sure it is a column

		% Now here I reconstitute s - complex frequency - and 
		% evaluate again.  
		bottomvalue = po(ii)-sqrt(-1)*r(ii);  % take magnitude of imag part
		if (ii ==  originflag)  % if this is an origin point
			bottomvalue = 0;
		end; 
	
		topvalue = po(ii)+sqrt(-1)*r(ii); % the top value where detour stops
		nbegin = find(imag(w)imag(topvalue));  % find all points greater than 
		nnend = length(nend);    % encirclement around jw root
		
		if (nnend == 0) 
			send = 0	
		else send = w(nend);
		end
	
		w = [sbegin; s1; send];  % reconstituted half of jw axis
	end 
else
	w = sqrt(-1)*w;
end

% this ends the loop for imaginary axis poles

% *************************************************************
% back to the regular nyquist program here

% Compute frequency response
if (nargin==2)|(nargin==3)
	gt = freqresp(num,den,w);
else
	gt = freqresp(a,b,c,d,iu,w);
end

ret=real(gt); 
imt=imag(gt);

% If no left hand arguments then plot graph.
if nargout==0,
	status = ishold;
	plot(ret,imt,'r-',ret,-imt,'g--')  

	%  plot(real(w),imag(w))	
	
	% modifications added here to count encirclements
	[numc,denc] = tfchk(num,den);
	gw = [(ret + j*imt); numc(1)/denc(1); (flipud(ret) - j*flipud(imt))]; % look at G(jw)
	[Ngw,Mgw] = size(gw);  % size of evaluated G(jw)
	gwp1 = gw + ones(Ngw,Mgw);
	% moves origin from 0 to -1, so we can count encirclements around -1 now
	
	initial_angle = rem(180/pi*angle(gwp1(1)) + 360, 360); % define initial angle
	angle_gwp1 = rem(180/pi*angle(gwp1) - initial_angle + 720,360); % angle from origin for all points
	dagw = diff(angle_gwp1);  % subtract off initial angle find degrees of encirclement
	tolerance = 180;  % define tolerance - where encirclement counter "flips" over
	Ipd = find(dagw < -tolerance);  % number of anti-clockwise encirclements
	Ind = find(dagw > tolerance); % number of clockwise encirclemtents
	Nacw = max(size(Ipd)) - max(size(Ind));   % number of encirclements

	% Nyquist Criterion says  Z = P - N
	P = length(find(p>0))
	disp('P = number of Open loop poles in rhp');
	
	N = Nacw      
	disp('N =  number of anti-clockwise encirclements')

	Z = P - N     % 
	disp('Z = number of closed loop poles in rhp')	

   	set(gca, 'YLimMode', 'auto')
   	limits = axis;

  	% Set axis hold on because next plot command may rescale
  	 set(gca, 'YLimMode', 'auto')
  	 set(gca, 'XLimMode', 'manual')
 	  hold on

	% Make arrows
	for k=1:size(gt,2),
		g = gt(:,k);
		re = ret(:,k);
		im = imt(:,k);
		sx = limits(2) - limits(1);
		[sy,sample]=max(abs(2*im));
		arrow=[-1;0;-1] + 0.75*sqrt(-1)*[1;0;-1];
		sample=sample+(sample==1);
		reim=diag(g(sample,:));
		d=diag(g(sample+1,:)-g(sample-1,:)); 
		
		% Rotate arrow taking into account scaling factors sx and sy
		d = real(d)*sy + sqrt(-1)*imag(d)*sx;
		rot=d./abs(d);  	% Use this when arrow is not horizontal
		arrow = ones(3,1)*rot'.*arrow;
		scalex = (max(real(arrow)) - min(real(arrow)))*sx/50;
		scaley = (max(imag(arrow)) - min(imag(arrow)))*sy/50;
		arrow = real(arrow)*scalex + sqrt(-1)*imag(arrow)*scaley;
		xy =ones(3,1)*reim' + arrow;
		xy2=ones(3,1)*reim' - arrow;
		[m,n]=size(g); 
		hold on
		plot(real(xy),-imag(xy),'r-',real(xy2),imag(xy2),'g-')
	end
   
	xlabel('Real Axis'), ylabel('Imag Axis')
	limits = axis;

	% Make cross at s = -1 + j0, i.e the -1 point
	if limits(2) >= -1.5  & limits(1) <= -0.5 % Only plot if -1 point is not far out.
		line1 = (limits(2)-limits(1))/50;
		line2 = (limits(4)-limits(3))/50;
		plot([-1+line1, -1-line1], [0,0], 'w-', [-1, -1], [line2, -line2], 'w-')
	end

	% Axis
	plot([limits(1:2);0,0]',[0,0;limits(3:4)]','w:');

	if ~status, hold off,end	% Return hold to previous status
	return % Suppress output
end

%reout = ret; 
%plot(real(p),imag(p),'x',real(z),imag(z),'o');


Use your browser's "Back" button to return to the previous page.

8/29/96 LO