
%%% example of a particle filter
%%% CS148 -- Building Intelligent Robots
%%% Author: Chad Jenkins  (cjenkins@cs.brown.edu)
%%%         Yuri Malitsky (ynm@cs.brown.edu)
%%% Modified: February 21, 2009
%%%
%%% call as function with 2-5 arguments:
%%% diffusion_strength: strength of the uncertainty of movement (0-4)
%%% step_by_step: set to 1 to stop after every step (0,1)
%%% truth_movement_range: set the true start and end location of the robot's motion (optional) ([1.1, 1.9] by default)
%%% truth_odometry: set the odometry step size (optional) (-.01 by default) 
%%% truth_doors: set the positions of the doors in the world (optional) ([[1.15; 1.22] [1.55; 1.62] [1.67; 1.74]] by default)
%%%
%%%
%%% Internal Variables:
%%% nx: all posible positions of the robot
%%% bin: probability that the robot is at agiven position
%%% precision: number of sample points
%%% odometry: amount the robot moves after every step
%%%
%%% based on Thrun's tutorial on Probabilistic Robotics

%based on Thrun's tutorial on Probabilistic Robotics

%assume sensed odometry is true odometry

function [nul] = particlefilter_hallway( diffusion_strength, step_by_step, truth_movement_range, truth_odometry, truth_doors )

hold off

welcome = 'Hello World'

% initialization: particle hypotheses, uniform probabilities, doors,
%    odometry step size
precision = 500;
x = 1:(1/precision):2; %1:0.005:2;
nx = 1:(1/precision):2;


% set the environment as the positions of the doors along the hallway
if (~exist('diffusion_strength'))
   diffusion_strength = 1;
end

% set the environment as the positions of the doors along the hallway
if (~exist('step_by_step'))
   step_by_step = 0;
end

% set the environment as the positions of the doors along the hallway
if (exist('truth_doors'))
   doors = truth_doors;
else
   doors = [[1.15; 1.22] [1.55; 1.62] [1.67; 1.74]]; %default environment
end

% set the true start and end location of the robot's motion
if (exist('truth_movement_range'))
   movement_range = truth_movement_range;
   x = movement_range(1):(1/precision):movement_range(2); %1:0.005:2;
   nx = movement_range(1):(1/precision):movement_range(2);
else
   movement_range = [1.9 1.1]; %default robot movement range
end

% set odometry step size
if (exist('truth_odometry'))
   odometry = truth_odometry;
else
   odometry = -0.01; % default odometry step
end

% probabilities
px = ones(size(x));
px = px / sum(px);

bin = ones(size(x));
bin = bin / sum(bin);


% Scan from hallway from right to left
for truex = movement_range(1,1):odometry:movement_range(1,2)
   % sensing from true position along hallway
   sense_door = [0, 0, 0];
   for i = 1:size(doors,2)
       if ((truex-0.02 >= doors(1,i)) & (truex-0.02 <= doors(2,i)))
           sense_door(1) = 1;
       end

       if ((truex >= doors(1,i)) & (truex <= doors(2,i)))
           sense_door(2) = 1;
       end

       if ((truex+0.02 >= doors(1,i)) & (truex+0.02 <= doors(2,i)))
           sense_door(3) = 1;
       end
   end

   % resample particles with replacement by dynamics update
   old_x = x;
   importancebin = cumsum(px);
   importancebin = importancebin/max(importancebin);
   random_samples = rand(size(importancebin));
   for i = 1:length(random_samples)
       % find the next bin
       binassign = min(find(importancebin >= random_samples(1,i)));

       % new sample = old pos + drift + diffusion
       x(1,i) = old_x(1,binassign) + odometry + diffusion_strength*odometry*(2*rand-1.0);
       b(1,i) = binassign;

       if x(1,i) < 1
           x(1,i) = 1;
       end

       if x(1,i) > 2
           x(1,i) = 2;
       end

       % the last 5 samples are random
       if (i > length(random_samples)-5) % add some uniformly random particles
          x(1,i) = 1.5+1.0*(rand-0.5);
          b(1,i) = binassign;
       end
   end

   % evaluate likelihood of each particle
   temp_px = px(1,b);
   for i = 1:length(x)
       px(1,i) = hallway_likelihood(sense_door, x(1,i), doors);
   end

   % sum up the scores for each bin
   temp_bin = bin(1,b);
   bin = zeros(size(x));
   for i = 1:length(x)
       pos = round((x(1,i)-1)*precision) + 1;
       bin(1, pos) = bin(1, pos) + px(1,i);
   end
   bin(1,1) = 0;
   bin(1,precision+1) = 0;

   % normalize distribution over the bins
   bin = bin / sum(bin);
   draw_world( doors, truex, sense_door, nx, temp_bin, bin );

   if step_by_step > 0
       input('Step');
   end
end

end

function [lhood] = hallway_likelihood(sense_door, belief_position, door_intervals)
   lhood = 0;

   believe_door = 0;
   for i = 1:size(door_intervals,2)
       if ((belief_position >= door_intervals(1,i)) & (belief_position <= door_intervals(2,i)))
           believe_door = 1;
       end
   end

   if (sense_door == believe_door)
       lhood = 10;
   end

   c_case = sense_door(1)*power(2,0) + sense_door(2)*power(2,1) + sense_door(3)*power(2,2);
   for i = 1:size(door_intervals,2)
       switch( c_case )
           case 1  % 1 0 0
               if ((belief_position >= door_intervals(2,i)-0.03) & (belief_position <= door_intervals(2,i)+0.06))
                   lhood = 10;
               end
           case 3  % 1 1 0
               if ((belief_position >= door_intervals(2,i)-0.06) & (belief_position <= door_intervals(2,i)+0.03))
                   lhood = 10;
               end
           case 4  % 0 0 1
               if ((belief_position >= door_intervals(1,i)-0.06) & (belief_position <= door_intervals(1,i)+0.03))
                   lhood = 10;
               end
           case 6  % 0 1 1
               if ((belief_position >= door_intervals(1,i)-0.03) & (belief_position <= door_intervals(1,i)+0.06))
                   lhood = 10;
               end
       end
   end
end

function [] = draw_world( doors, truex, sense_door, x, temp_px, px )
   % draw bounding frame
   hold off;
   plot(1,1,'w.');
   hold on;
   line([0.5 2.5],[1 1],'Color', [0 0 0]);

   % draw doors
   for i = 1:size(doors,2)
       fill([doors(1,i) doors(1,i) doors(2,i) doors(2,i)],[1 1.3 1.3 1], 'r');
       fill([doors(2,i)-.015 doors(2,i)-.015 doors(2,i)-0.005 doors(2,i)-0.005],[1.14 1.16 1.16 1.14], 'w');
   end

   % draw true robot state
   fill([truex-0.03  truex-0.03  truex+0.03  truex+0.03], [0.7  0.80 0.80 0.7],  'g');
   fill([truex-0.027 truex-0.027 truex-0.007 truex-0.007],[0.68 0.72 0.72 0.68], 'b');
   fill([truex+0.027 truex+0.027 truex+0.007 truex+0.007],[0.68 0.72 0.72 0.68], 'b');

   % draw the beams of the sensors
   if (sense_door(1))
       plot([truex-0.02 truex-0.02], [0.8 1.15], ':','LineWidth',2,'Color',[0.5 0 0]);
   else
       plot([truex-0.02 truex-0.02], [0.8 1.15], ':','LineWidth',2,'Color',[0.5 0.5 0.5]);
   end

   if (sense_door(2))
       plot([truex truex], [0.8 1.15], ':','LineWidth',2,'Color',[0.5 0 0]);
   else
       plot([truex truex], [0.8 1.15], ':','LineWidth',2,'Color',[0.5 0.5 0.5]);
   end

   if (sense_door(3))
       plot([truex+0.02 truex+0.02], [0.8 1.15], ':','LineWidth',2,'Color',[0.5 0 0]);
   else
       plot([truex+0.02 truex+0.02], [0.8 1.15], ':','LineWidth',2,'Color',[0.5 0.5 0.5]);
   end

   scale = max(px);
   for i = 1:length(x)
       plot([x(1,i) x(1,i)],[0 0.5*px(1,i)/scale], 'r');
   end

   % estimate robot's position as mean of particle distribution
   %meanx = 0;
   %for i = 1:length(x)
   %   meanx = meanx + x(1,i)*px(1,i);
   %end

   % draw robot's estimated position
   %fill([meanx-0.03  meanx-0.03  meanx+0.03  meanx+0.03], [1.4  1.55 1.55 1.4],  'g');
   %fill([meanx-0.027 meanx-0.027 meanx-0.007 meanx-0.007],[1.38 1.42 1.42 1.38], 'b');
   %fill([meanx+0.027 meanx+0.027 meanx+0.007 meanx+0.007],[1.38 1.42 1.42 1.38], 'b');

   % refresh display
   refresh;
   drawnow;
end