Code covered by the BSD License  

Highlights from
Sim.I.am

image thumbnail
from Sim.I.am by Jean-Pierre de la Croix
A MATLAB-based educational bridge between theory and practice in robotics.

simiam.controller.khepera3.K3Supervisor
classdef K3Supervisor < simiam.controller.Supervisor
%% SUPERVISOR switches between controllers and handles their inputs/outputs.
%
% Properties:
%   current_controller      - Currently selected controller
%   controllers             - List of available controllers
%   goal_points             - Set of goal points
%   goal_index              - Pointer to current goal point
%   v                       - Robot velocity
%
% Methods:
%   execute - Selects and executes the current controller.

% Copyright (C) 2013, Georgia Tech Research Corporation
% see the LICENSE file included with this software

    properties
    %% PROPERTIES
    
        states
        eventsd
        
        current_state
    
    
        prev_ticks          % Previous tick count on the left and right wheels
        v
        goal
        goal_prev
        d_stop
        d_at_obs
        d_unsafe
        d_prog
        p
        
        direction
        
        v_gtg
        v_ao
        v_fw
        
%         is_init
    end
    
    methods
    %% METHODS
        
        function obj = K3Supervisor()
        %% SUPERVISOR Constructor
            obj = obj@simiam.controller.Supervisor();
            
            % initialize the controllers
            obj.controllers{1} = simiam.controller.AvoidObstacles();
            obj.controllers{2} = simiam.controller.GoToGoal();
            obj.controllers{3} = simiam.controller.GoToAngle();
            obj.controllers{4} = simiam.controller.AOandGTG();
            obj.controllers{5} = simiam.controller.Stop();
            obj.controllers{6} = simiam.controller.FollowWall();
            obj.controllers{7} = simiam.controller.SlidingMode();
            
            % set the initial controller
            obj.current_controller = obj.controllers{2};
            obj.current_state = 2;
            
            % generate the set of states
            for i = 1:length(obj.controllers)
                obj.states{i} = struct('state', obj.controllers{i}.type, ...
                                       'controller', obj.controllers{i});
            end
            
            % define the set of eventsd
            obj.eventsd{1} = struct('event', 'at_obstacle', ...
                                   'callback', @at_obstacle);
            
            obj.eventsd{2} = struct('event', 'at_goal', ...
                                   'callback', @at_goal);
            
            obj.eventsd{3} = struct('event', 'obstacle_cleared', ...
                                    'callback', @obstacle_cleared);
                                
            obj.eventsd{4} = struct('event', 'unsafe', ...
                                    'callback', @unsafe);
                                
            obj.eventsd{5} = struct('event', 'progress_made', ...
                                    'callback', @progress_made);
                                
            obj.eventsd{6} = struct('event', 'sliding_left', ...
                                    'callback', @sliding_left);
                               
            obj.eventsd{7} = struct('event', 'sliding_right', ...
                                    'callback', @sliding_right);
                               
            obj.prev_ticks = struct('left', 0, 'right', 0);
            
            obj.v               = 0.1;
            
            obj.goal            = [1;1];
            obj.goal_prev       = obj.goal;
            obj.d_stop          = 0.05; 
            obj.d_at_obs        = 0.10;                
            obj.d_unsafe        = 0.03;
            
            obj.d_prog = 10;
            
            obj.p = simiam.util.Plotter();
            obj.current_controller.p = obj.p;
            
            obj.direction = 'right';
            
%             obj.is_init = false;
        end
        
        function execute(obj, dt)
        %% EXECUTE Selects and executes the current controller.
        %   execute(obj, robot) will select a controller from the list of
        %   available controllers and execute it.
        %
        %   See also controller/execute
            
%             if(~obj.is_init)
%                 hold(obj.robot.parent, 'on');
%                 obj.v_gtg = plot(obj.robot.parent, [0 0], [0 0], 'b-');
%                 obj.v_ao = plot(obj.robot.parent, [0 0], [0 0], 'r-');
%                 obj.v_fw = plot(obj.robot.parent, [0 0], [0 0], 'g-');
%                 obj.is_init = true;
%             end
        
            inputs = obj.controllers{7}.inputs; 
            inputs.x_g = obj.goal(1);
            inputs.y_g = obj.goal(2);

            
            if(~all(obj.goal==obj.goal_prev))
%                 [x,y,theta] = obj.state_estimate.unpack();
%                 if(norm(obj.goal-[x;y])<norm(obj.goal_prev-[x;y]))
                    obj.set_progress_point();
%                 end
                obj.goal_prev = obj.goal;
                obj.switch_to_state('go_to_goal');
            end
            
            if (obj.check_event('at_goal'))
                obj.switch_to_state('stop');
%                 [x,y,theta] = obj.state_estimate.unpack();
%                 fprintf('stopped at (%0.3f,%0.3f)\n', x, y);
            elseif(obj.check_event('unsafe'))
                obj.switch_to_state('avoid_obstacles');                
            else
                if (obj.is_in_state('go_to_goal'))
                    if(obj.check_event('at_obstacle') && obj.check_event('sliding_left'))
                        obj.direction = 'left';
%                         fprintf('now following to the left\n');
                        obj.switch_to_state('follow_wall');
                        obj.set_progress_point();
                    elseif(obj.check_event('at_obstacle') && obj.check_event('sliding_right'))
                        obj.direction = 'right';
%                         fprintf('now following to the right\n');
                        obj.switch_to_state('follow_wall');
                        obj.set_progress_point();
                    end
                elseif (obj.is_in_state('follow_wall') && strcmp(obj.direction,'left'))
                    if(obj.check_event('progress_made') && ~obj.check_event('sliding_left'))
                        obj.switch_to_state('go_to_goal');
                    end
                elseif (obj.is_in_state('follow_wall') && strcmp(obj.direction, 'right'))
                    if(obj.check_event('progress_made') && ~obj.check_event('sliding_right'))
                        obj.switch_to_state('go_to_goal');
                    end
                elseif (obj.is_in_state('avoid_obstacles'))
                    if(obj.check_event('obstacle_cleared'))
%                         if(obj.check_event('sliding_left'))
%                             obj.direction = 'left';
%                             obj.switch_to_state('follow_wall');
%                         elseif(obj.check_event('sliding_right'))
%                             obj.direction = 'right';
%                             obj.switch_to_state('follow_wall');
%                         else
                            obj.switch_to_state('go_to_goal');
%                         end
                    end
                end
 
            end
            
            inputs.direction = obj.direction;
                                    
            outputs = obj.current_controller.execute(obj.robot, obj.state_estimate, inputs, dt);
                
            [vel_r, vel_l] = obj.robot.dynamics.uni_to_diff(outputs.v, outputs.w);
            obj.robot.set_wheel_speeds(vel_r, vel_l);
                        
            obj.update_odometry();
%             [x, y, theta] = obj.state_estimate.unpack();
%             fprintf('current_pose: (%0.3f,%0.3f,%0.3f)\n', x, y, theta);
        end
        
        function set_progress_point(obj)
            [x, y, theta] = obj.state_estimate.unpack();
            obj.d_prog = norm([x-obj.goal(1);y-obj.goal(2)]);
        end
        
        %% Events %%
        
        function rc = sliding_left(obj, state, robot)
            inputs = obj.controllers{7}.inputs;
            inputs.x_g = obj.goal(1);
            inputs.y_g = obj.goal(2);
            inputs.direction = 'left';
            
            obj.controllers{7}.execute(obj.robot, obj.state_estimate, inputs, 0);
            
            u_gtg = obj.controllers{7}.u_gtg;
            u_ao = obj.controllers{7}.u_ao;
            u_fw = obj.controllers{7}.u_fw;
            
%             set(obj.v_gtg, 'XData', [0 u_gtg(1)]);
%             set(obj.v_gtg, 'YData', [0 u_gtg(2)]);
%             set(obj.v_ao,  'XData', [0 u_ao(1)]);
%             set(obj.v_ao,  'YData', [0 u_ao(2)]);
%             set(obj.v_fw,  'XData', [0 u_fw(1)]);
%             set(obj.v_fw,  'YData', [0 u_fw(2)]);
            
            sigma = [u_gtg u_ao]\u_fw;
%             fprintf('sliding left check: (%0.3f,%0.3f)\n', sigma(1), sigma(2));
            rc = false;
            if sigma(1) > 0 && sigma(2) > 0
%                 fprintf('now sliding left\n');
                rc = true;
            end
        end
        
        function rc = sliding_right(obj, state, robot)
            inputs = obj.controllers{7}.inputs;
            inputs.x_g = obj.goal(1);
            inputs.y_g = obj.goal(2);
            inputs.direction = 'right';
            
            obj.controllers{7}.execute(obj.robot, obj.state_estimate, inputs, 0);
            
            u_gtg = obj.controllers{7}.u_gtg;
            u_ao = obj.controllers{7}.u_ao;
            u_fw = obj.controllers{7}.u_fw;
            
            sigma = [u_gtg u_ao]\u_fw;
%             fprintf('sliding right check: (%0.3f,%0.3f)\n', sigma(1), sigma(2)); 
            
            rc = false;
            if sigma(1) > 0 && sigma(2) > 0
%                 fprintf('now sliding right\n');
                rc = true;
            end
        end   
        
        function rc = at_obstacle(obj, state, robot)
            ir_distances = obj.robot.get_ir_distances();
            rc = false;                                     % Assume initially that the robot is clear of obstacle
            
            % Loop through and test the sensors (only the front set)
            if any(ir_distances(2:7) < obj.d_at_obs)
                rc = true;
            end
        end
        
        function rc = unsafe(obj, state, robot)
            ir_distances = obj.robot.get_ir_distances();              
            rc = false;             % Assume initially that the robot is clear of obstacle
            
            % Loop through and test the sensors (only the front set)
            if any(ir_distances(2:7) < obj.d_unsafe)
                    rc = true;
            end
        end

        function rc = at_goal(obj, state, robot)
            [x,y,theta] = obj.state_estimate.unpack();
            rc = false;
            
            % Test distance from goal
            if norm([x - obj.goal(1); y - obj.goal(2)]) < obj.d_stop
                rc = true;
            end
        end

        function rc = obstacle_cleared(obj, state, robot)
            ir_distances = obj.robot.get_ir_distances();
            rc = false;              % Assume initially that the robot is clear of obstacle
            
            % Loop through and test the sensors (only front set)
            if all(ir_distances(2:7) > obj.d_at_obs)
                rc = true;
            end
        end
        
        function rc = progress_made(obj, state, robot)

            % Check for any progress
            [x, y, theta] = obj.state_estimate.unpack();
            epsilon = 0.1;
            
            rc = false;
            if norm([obj.goal(1)-x; obj.goal(2)-y]) < obj.d_prog-epsilon
                rc = true;          % progress has been made
            end
            
        end
        
        
        %% State machine support functions
        
        function set_current_controller(obj, ctrl)
            % save plots
            obj.current_controller = ctrl;
            obj.p.switch_2d_ref();
            obj.current_controller.p = obj.p;
        end
        
        function rc = is_in_state(obj, name)
            rc = strcmp(name, obj.states{obj.current_state}.state);
        end
        
        function switch_to_state(obj, name)
            
            if(~obj.is_in_state(name))
                for i=1:numel(obj.states)
                    if(strcmp(obj.states{i}.state, name))
                        obj.set_current_controller(obj.states{i}.controller);
                        obj.current_state = i;
                        fprintf('switching to state %s\n', name);
                        return;
                    end
                end
            else
%                 fprintf('already in state %s\n', name);
                return
            end
            
            fprintf('no state exists with name %s\n', name);
        end
        
        function rc = check_event(obj, name)
           for i=1:numel(obj.eventsd)
               if(strcmp(obj.eventsd{i}.event, name))
                   rc = obj.eventsd{i}.callback(obj, obj.states{obj.current_state}, obj.robot);
                   return;
               end
           end
           
           % return code (rc)
           fprintf('no event exists with name %s\n', name);
           rc = false;
        end
        
        %% Odometry
        
        function update_odometry(obj)
        %% UPDATE_ODOMETRY Approximates the location of the robot.
        %   obj = obj.update_odometry(robot) should be called from the
        %   execute function every iteration. The location of the robot is
        %   updated based on the difference to the previous wheel encoder
        %   ticks. This is only an approximation.
        %
        %   state_estimate is updated with the new location and the
        %   measured wheel encoder tick counts are stored in prev_ticks.
        
            % Get wheel encoder ticks from the robot
            right_ticks = obj.robot.encoders(1).ticks;
            left_ticks = obj.robot.encoders(2).ticks;
            
            % Recal the previous wheel encoder ticks
            prev_right_ticks = obj.prev_ticks.right;
            prev_left_ticks = obj.prev_ticks.left;
            
            % Previous estimate 
            [x, y, theta] = obj.state_estimate.unpack();
            
            % Compute odometry here
            R = obj.robot.wheel_radius;
            L = obj.robot.wheel_base_length;
            m_per_tick = (2*pi*R)/obj.robot.encoders(1).ticks_per_rev;
            
            d_right = (right_ticks-prev_right_ticks)*m_per_tick;
            d_left = (left_ticks-prev_left_ticks)*m_per_tick;
            d_center = (d_right + d_left)/2;
            
            x_dt = d_center*cos(theta);
            y_dt = d_center*sin(theta);
            theta_dt = (d_right - d_left)/L;
            
            theta_new = theta + theta_dt;
            x_new = x + x_dt;
            y_new = y + y_dt;                           
%             fprintf('Estimated pose (x,y,theta): (%0.3g,%0.3g,%0.3g)\n', x_new, y_new, theta_new);
            
            % Save the wheel encoder ticks for the next estimate
            obj.prev_ticks.right = right_ticks;
            obj.prev_ticks.left = left_ticks;
            
            % Update your estimate of (x,y,theta)
            obj.state_estimate.set_pose([x_new, y_new, theta_new]);
        end
    end
end

Contact us