% control_1D_HJB.m % This program solves the HJB equation for % a simpe 1-D optimal control problem using % upwind finite differencing. % % INPUT arguments % --------------- % SYS: definition of system and tuning parameters % SYS.tH: horizon time % SYS.CU: penalty weight for control inputs % SYS.CH: horizon-time penalty weight % SYS.xSet: set-point of system % SYS.uSet: control input that achieves set-point steady-state % SYS.numReport: number of times at which Bellman function is % to be reported % GRID: definition of numerical grid for HJB FD calculation % GRID.N: number of point in the grid % GRID.xLo: lower-bound on computational domain % GRID.xHi: upper-bound on computational domain % % OUTPUT arguments % ---------------- % UC: optimal control law % UC.x: x-values % UC.u: u-values for optimal control law % UC.K: effective proportional controller gain % % Kenneth J. Beers % MIT Department of Chemical Engineering % June 6, 2004 function [UC,TRAJ,iflag] = control_1D_HJB(SYS,GRID); iflag = 0; % set default values if necessary % system parameters if(exist('SYS') ~= 1) SYS = []; end if(~isfield(SYS,'tH')) SYS.tH = 10; end if(~isfield(SYS,'CU')) SYS.CU = 1; end if(~isfield(SYS,'CH')) SYS.CH = 10; end if(~isfield(SYS,'xSet')) SYS.xSet = 2; end if(~isfield(SYS,'uSet')) SYS.uSet = 1; end if(~isfield(SYS,'numReport')); SYS.numReport = 50; end % grid parameters if(exist('GRID') ~= 1) GRID = []; end if(~isfield(GRID,'N')) GRID.N = 100; end if(~isfield(GRID,'xLo')) GRID.xLo = -10; end if(~isfield(GRID,'xHi')) GRID.xHi = 10; end % initialize numerical grid values GRID.x = linspace(GRID.xLo,GRID.xHi,GRID.N)'; GRID.dx = GRID.x(2)-GRID.x(1); % compute finite difference matrix based on forward difference % for x<=0 and backward difference for x>0 GRID.A = spalloc(GRID.N,GRID.N,2); for k=1:GRID.N if(GRID.x(k) <= 0) % forward difference GRID.A(k,k+1) = 1; GRID.A(k,k) = -1; else % backward difference GRID.A(k,k) = 1; GRID.A(k,k-1) = -1; end end GRID.A = GRID.A./GRID.dx; % get initial values of Bellman function at tau = 0 phi0 = SYS.CH.*(GRID.x - SYS.xSet).^2; % set tau values at which the Bellman function is to % be reported tau_report = linspace(0,SYS.tH,SYS.numReport); % call ode23s to solve HJB equation [TRAJ.tau,TRAJ.phi] = ode23s(@phi_dot,tau_report,phi0,[],SYS,GRID); % plot results of Bellman function trajectory [XX,TT] = meshgrid(GRID.x,TRAJ.tau); figure; surf(XX,TT,TRAJ.phi); xlabel('x'); ylabel('tau'); title('Bellman function \phi(\tau,x)'); % reconstruct optimal control law UC.x = GRID.x; [phi_dot,UC.u] = phi_dot(SYS.tH,TRAJ.phi(SYS.numReport,:)',SYS,GRID); figure; plot(UC.x,UC.u); hold on; % add a line at u = SYS.uSet plot([GRID.xLo GRID.xHi],[SYS.uSet SYS.uSet], 'k:'); % add a line at x = SYS.xSet plot([SYS.xSet SYS.xSet], [min(UC.u) max(UC.u)], 'k-.'); xlabel('x'); ylabel('u'); axis tight; title('Optimal control law'); % Fit the resulting control law to a simple proportional controller e = UC.x - SYS.xSet; % departure from set point UC.K = dot(e,UC.u-SYS.uSet)/dot(e,e); iflag = 1; return; % ------------------------------ % This routine returns the tau derivative of the Bellman function. % An optimal second output is the optimal control values function [phi_dot,u] = phi_dot(tau,phi,SYS,GRID); % compute first-pass of derivatives dphi_dx from finite differences dphi_dx = GRID.A*phi; % compute optimal control inputs u = SYS.uSet - dphi_dx./SYS.CU; % compute time derivative vector f = -(GRID.x-1) + u; % recompute derivatives to ensure upwind differencing for k=2:(GRID.N-1) % if f >0, use forward difference if(f(k) > 0) dphi_dx(k) = (phi(k+1)-phi(k))/GRID.dx; else % use backward difference dphi_dx(k) = (phi(k)-phi(k-1))/GRID.dx; end end % get new optimal control inputs and time derivative vector % based on change in direction of differences u = SYS.uSet - dphi_dx./SYS.CU; f = -(GRID.x-1) + u; % compute tau derivatives of Bellman function phi_dot = (SYS.CU/2).*(u - SYS.uSet).^2 ... + (GRID.x - SYS.xSet).^2 ... + dphi_dx.*f; return;