%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Multilayer Perceptron for Tanker Ship Heading Regulation
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% By: Kevin Passino 
% Version: 4/4/00
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clear		% Clear all variables in memory

% Initialize ship parameters 
% (can test two conditions, "ballast" or "full"):

ell=350;			% Length of the ship (in meters)
u=5;				% Nominal speed (in meters/sec)
%u=3;               % A lower speed where the ship is more difficult to control
abar=1;             % Parameters for nonlinearity
bbar=1;

% The parameters for the tanker under "ballast" conditions 
% (a heavy ship) are:

K_0=5.88;
tau_10=-16.91;
tau_20=0.45;
tau_30=1.43;

% The parameters for the tanker under "full" conditions (a ship
% that weighs less than one under "ballast" conditions) are:

%K_0=0.83;
%tau_10=-2.88;
%tau_20=0.38;
%tau_30=1.07;

% Some other plant parameters are:

K=K_0*(u/ell);
tau_1=tau_10*(ell/u);
tau_2=tau_20*(ell/u);
tau_3=tau_30*(ell/u);

% Parameters for the multilayer perceptron

% The first hidden layer is trivial, with unity weights and a zero bias 

% Weights/biases in the second hidden layer:

w112=10;
w122=10;
b12=-200*pi/180;
b22=+200*pi/180;

% Weights/biases in the third hidden layer:

w113=-80*pi/180;
w223=-80*pi/180;
b13=0;
b23=80*pi/180;

% The bias in the output layer is zero and the two
% output layer weights are unity.
		

% Now, you can proceed to do the simulation or simply view the nonlinear
% surface generated by the neural controller. 

%flag1=input('\n Do you want to simulate the \n neural control system \n for the tanker?  \n (type 1 for yes and 0 for no) ');

%if flag1==1, 
	
% Next, we initialize the simulation:

t=0; 		% Reset time to zero
index=1;	% This is time's index (not time, its index).  
tstop=4000;	% Stopping time for the simulation (in seconds)
step=1;     % Integration step size
T=10;		% The controller is implemented in discrete time and
			% this is the sampling time for the controller.
			% Note that the integration step size and the sampling
			% time are not the same.  In this way we seek to simulate
			% the continuous time system via the Runge-Kutta method and
			% the discrete time controller as if it were
			% implemented by a digital computer.  Hence, we sample
			% the plant output every T seconds and at that time
			% output a new value of the controller output.
counter=10;	% This counter will be used to count the number of integration
			% steps that have been taken in the current sampling interval.
			% Set it to 10 to begin so that it will compute a controller
			% output at the first step.
			% For our example, when 10 integration steps have been
			% taken we will then we will sample the ship heading
			% and the reference heading and compute a new output
			% for the controller.  
x=[0;0;0];	% First, set the state to be a vector            
x(1)=0;		% Set the initial heading to be zero
x(2)=0;		% Set the initial heading rate to be zero.  
			% We would also like to set x(3) initially but this
			% must be done after we have computed the output
			% of the controller.  In this case, by
			% choosing the reference trajectory to be 
			% zero at the beginning and the other initial conditions
			% as they are, and the controller as designed,
			% we will know that the output of the controller
			% will start out at zero so we could have set 
			% x(3)=0 here.  To keep things more general, however, 
			% we set the intial condition immediately after 
			% we compute the first controller output in the 
			% loop below.


% Next, we start the simulation of the system.  This is the main 
% loop for the simulation of the control system.

while t <= tstop

% First, we define the reference input psi_r  (desired heading).

if t<100, psi_r(index)=0; end    			% Request heading of 0 deg
if t>=100, psi_r(index)=45*(pi/180); end     % Request heading of 45 deg
if t>2000, psi_r(index)=0; end      			% Then request heading of 0 deg
%if t>4000, psi_r(index)=45*(pi/180); end     % Then request heading of 45 deg
%if t>6000, psi_r(index)=0; end      			% Then request heading of 0 deg
%if t>8000, psi_r(index)=45*(pi/180); end     % Then request heading of 45 deg
%if t>10000, psi_r(index)=0; end      			% Then request heading of 0 deg
%if t>12000, psi_r(index)=45*(pi/180); end     % Then request heading of 45 deg

% Next, suppose that there is sensor noise for the heading sensor with that is
% additive, with a uniform distribution on [- 0.01,+0.01] deg.
%s(index)=0.01*(pi/180)*(2*rand-1);
s(index)=0;					  % This allows us to remove the noise.

psi(index)=x(1)+s(index);     % Heading of the ship (possibly with sensor noise).

if counter == 10,  % When the counter reaches 10 then execute the 
				   % controller

counter=0; 			% First, reset the counter

% Multilayer perceptron controller calculations:

e(index)=psi_r(index)-psi(index); % Computes error (first layer of perceptron)

xbar1(index)=b12+w112*e(index); % Compute inputs to activation functions in second hidden layer
xbar2(index)=b22+w122*e(index);
x11(index)=1/(1+exp(-xbar1(index))); % Compute output of activation functions
x21(index)=1/(1+exp(-xbar2(index)));
delta(index)=(b13+w113*x11(index)+b23+w223*x21(index)); % Compute second hidden layer and output layer

%
% A conventinal proportional controller:
%delta(index)=-e(index);

else % This goes with the "if" statement to check if the counter=10
     % so the next lines up to the next "end" statement are executed
     % whenever counter is not equal to 10

% Now, even though we do not compute the neural controller at each
% time instant, we do want to save the data at its inputs and output at
% each time instant for the sake of plotting it.  Hence, we need to 
% compute these here (note that we simply hold the values constant):

e(index)=e(index-1);	
delta(index)=delta(index-1);

end % This is the end statement for the "if counter=10" statement

% Next, comes the plant:
% Now, for the first step, we set the initial condition for the
% third state x(3).

if t==0, x(3)=-(K*tau_3/(tau_1*tau_2))*delta(index); end

% Next, the Runge-Kutta equations are used to find the next state. 
% Clearly, it would be better to use a Matlab "function" for
% F (but here we do not, so we can have only one program).
  
	time(index)=t;

% First, we define a wind disturbance against the body of the ship
% that has the effect of pressing water against the rudder

%w(index)=0.5*(pi/180)*sin(2*pi*0.001*t);  % This is an additive sine disturbance to 
										% the rudder input.  It is of amplitude of
										% 0.5 deg. and its period is 1000sec.
%delta(index)=delta(index)+w(index);


% Next, implement the nonlinearity where the rudder angle is saturated
% at +-80 degrees

if delta(index) >= 80*(pi/180), delta(index)=80*(pi/180); end
if delta(index) <= -80*(pi/180), delta(index)=-80*(pi/180); end

% Next, we use the formulas to implement the Runge-Kutta method
% (note that here only an approximation to the method is implemented where
% we do not compute the function at multiple points in the integration step size).

F=[ x(2) ;
    x(3)+ (K*tau_3/(tau_1*tau_2))*delta(index) ;
    -((1/tau_1)+(1/tau_2))*(x(3)+ (K*tau_3/(tau_1*tau_2))*delta(index))-...
        (1/(tau_1*tau_2))*(abar*x(2)^3 + bbar*x(2)) + (K/(tau_1*tau_2))*delta(index) ];
        
	k1=step*F;
	xnew=x+k1/2;

F=[ xnew(2) ;
    xnew(3)+ (K*tau_3/(tau_1*tau_2))*delta(index) ;
    -((1/tau_1)+(1/tau_2))*(xnew(3)+ (K*tau_3/(tau_1*tau_2))*delta(index))-...
        (1/(tau_1*tau_2))*(abar*xnew(2)^3 + bbar*xnew(2)) + (K/(tau_1*tau_2))*delta(index) ];
   
	k2=step*F;
	xnew=x+k2/2;

F=[ xnew(2) ;
    xnew(3)+ (K*tau_3/(tau_1*tau_2))*delta(index) ;
    -((1/tau_1)+(1/tau_2))*(xnew(3)+ (K*tau_3/(tau_1*tau_2))*delta(index))-...
        (1/(tau_1*tau_2))*(abar*xnew(2)^3 + bbar*xnew(2)) + (K/(tau_1*tau_2))*delta(index) ];
   
	k3=step*F;
	xnew=x+k3;

F=[ xnew(2) ;
    xnew(3)+ (K*tau_3/(tau_1*tau_2))*delta(index) ;
    -((1/tau_1)+(1/tau_2))*(xnew(3)+ (K*tau_3/(tau_1*tau_2))*delta(index))-...
        (1/(tau_1*tau_2))*(abar*xnew(2)^3 + bbar*xnew(2)) + (K/(tau_1*tau_2))*delta(index) ];
   
	k4=step*F;
	x=x+(1/6)*(k1+2*k2+2*k3+k4); % Calculated next state


t=t+step;  			% Increments time
index=index+1;	 	% Increments the indexing term so that 
					% index=1 corresponds to time t=0.
counter=counter+1;	% Indicates that we computed one more integration step

end % This end statement goes with the first "while" statement 
    % in the program so when this is complete the simulation is done.

%
% Next, we provide plots of the input and output of the ship 
% along with the reference heading that we want to track.
%

% First, we convert from rad. to degrees
psi_r=psi_r*(180/pi);
psi=psi*(180/pi);
delta=delta*(180/pi);
e=e*(180/pi);

% Next, we provide plots of data from the simulation

figure(1)
clf
subplot(311)
plot(time,psi,'k-',time,psi_r,'k--')
grid on
title('Ship heading (solid) and desired ship heading (dashed), deg.')
subplot(312)
plot(time,e,'k-')
grid on
title('Ship heading error between ship heading and desired heading, deg.')
subplot(313)
plot(time,delta,'k-')
grid on
xlabel('Time (sec)')
title('Rudder angle (\delta), deg.')
zoom


%end % This ends the if statement (on flag1) on whether you want to do a simulation
    % or just see the control surface 
	
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Next, provide a plot of the neural controller surface:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Request input from the user to see if they want to see the 
% controller mapping:

%flag2=input('\n Do you want to see the nonlinear \n mapping implemented by the neural \n controller? \n (type 1 for yes and 0 for no) ');

%if flag2==1,
	
% First, compute vectors with points over the whole range of 
% the neural controller inputs 


e_input=(-pi/2):(pi/2)/100:(pi/2); 

% Next, compute the neural controller output for all these inputs

for jj=1:length(e_input) 
		xbar1t(jj)=b12+w112*e_input(jj);
		xbar2t(jj)=b22+w122*e_input(jj);
		x11t(jj)=1/(1+exp(-xbar1t(jj)));
		x21t(jj)=1/(1+exp(-xbar2t(jj)));
		delta_output(jj)=b13+w113*x11t(jj)+b23+w223*x21t(jj);
		delta_output1(jj)=b13+w113*x11t(jj); % This line used to show only one path in the network
		delta_output2(jj)=b23+w223*x21t(jj); % This is used to show the second path
end

% Convert from radians to degrees:

delta_output=delta_output*(180/pi);
e_input=e_input*(180/pi);

delta_output1=delta_output1*(180/pi);
delta_output2=delta_output2*(180/pi);


figure(2)
clf
% Plot the two paths (from error to output)
subplot(311)
plot(e_input,delta_output1)
grid
ylabel('Output (\delta), deg.')
title('Top path multilayer perceptron mapping between error input and output')
axis([min(e_input) max(e_input) -80 80])
subplot(312)
plot(e_input,delta_output2)
grid
ylabel('Output (\delta), deg.')
title('Bottom path multilayer perceptron mapping between error input and output')
axis([min(e_input) max(e_input) -80 80])
% Plot the controller map from error to delta
subplot(313)
plot(e_input,delta_output)
grid
xlabel('Heading error (e), deg.')
ylabel('Output (\delta), deg.')
title('Multilayer perceptron mapping between error input and output')
axis([min(e_input) max(e_input) -80 80])

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Next, plot the two-dimensional surface of the multilayer perceptron:

psi_r_input=(-pi/2):(pi/2)/50:(pi/2); 
psi_input=(-pi/2):(pi/2)/50:(pi/2); 

% Next, compute the neural controller output for all these inputs

for jj=1:length(psi_r_input) 
	for ii=1:length(psi_input)
		e_input2(ii,jj)=psi_r_input(jj)-psi_input(ii);
		xbar1t2(ii,jj)=b12+w112*e_input2(ii,jj);
		xbar2t2(ii,jj)=b22+w122*e_input2(ii,jj);
		x11t2(ii,jj)=1/(1+exp(-xbar1t2(ii,jj)));
		x21t2(ii,jj)=1/(1+exp(-xbar2t2(ii,jj)));
		delta_outputt(ii,jj)=b13+w113*x11t2(ii,jj)+b23+w223*x21t2(ii,jj);
	end
end

% Convert from radians to degrees:

delta_outputt=delta_outputt*(180/pi);
psi_r_input=psi_r_input*(180/pi);
psi_input=psi_input*(180/pi);

% Plot the controller map

figure(3)
clf
surf(psi_r_input,psi_input,delta_outputt);
view(30,30)
colormap(white);
xlabel('Reference input (\psi_r), deg.');
ylabel('Heading angle (\psi), deg.');
zlabel('Controller output (\delta), deg.');
title('Multilayer perceptron controller mapping between inputs and output');


%end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% End of program %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
