├── utilities ├── OUT.mat ├── graph │ ├── html │ │ ├── cycleGL_eq12942671300991736101.png │ │ ├── lineGL_eq12942671300991736101.png │ │ ├── completeGL_eq08486233743349304198.png │ │ ├── completeGL_eq12942671300991736101.png │ │ ├── randomGL_eq12942671300991736101.png │ │ ├── delta_disk_neighbors_eq14324477822824332334.png │ │ ├── random_connectedGL_eq12942671300991736101.png │ │ ├── topological_neighbors_eq12941163982435923632.png │ │ ├── completeGL.html │ │ ├── cycleGL.html │ │ ├── lineGL.html │ │ ├── topological_neighbors.html │ │ ├── delta_disk_neighbors.html │ │ ├── randomGL.html │ │ └── random_connectedGL.html │ ├── completeGL.m │ ├── cycleGL.m │ ├── lineGL.m │ ├── topological_neighbors.m │ ├── delta_disk_neighbors.m │ ├── randomGL.m │ └── random_connectedGL.m ├── misc │ ├── html │ │ ├── generate_initial_conditions_eq07459738348397623860.png │ │ ├── generate_initial_conditions_eq16033661748480904678.png │ │ ├── unique_filename.html │ │ ├── generate_initial_conditions.html │ │ └── create_is_initialized.html │ ├── unique_filename.m │ ├── generate_initial_conditions.m │ └── create_is_initialized.m ├── transformations │ ├── html │ │ ├── create_si_to_uni_mapping2_eq12450574036302976132.png │ │ ├── create_si_to_uni_mapping_eq03927841599078721039.png │ │ ├── create_si_to_uni_mapping_eq06303456170493080000.png │ │ ├── create_si_to_uni_mapping_eq12450574036302976132.png │ │ ├── create_si_to_uni_mapping_eq13491430810795123718.png │ │ ├── create_uni_to_si_mapping_eq12450574036302976132.png │ │ ├── create_uni_to_si_mapping_eq13491430810795123718.png │ │ ├── create_si_to_uni_mapping2.html │ │ ├── create_uni_to_si_mapping.html │ │ └── create_si_to_uni_mapping.html │ ├── create_si_to_uni_mapping.m │ ├── create_si_to_uni_dynamics.m │ ├── create_si_to_uni_dynamics_with_backwards_motion.m │ └── create_uni_to_si_mapping.m ├── RobotariumError.m ├── getTargetPosition.m ├── controllers │ ├── create_automatic_parking_controller.m │ ├── create_si_position_controller.m │ ├── create_unicycle_position_controller.m │ ├── create_automatic_parking_controller2.m │ ├── create_parking_controller.m │ └── create_waypoint_controller.m ├── gritsbot_patch.m ├── barrier_certificates │ ├── create_si_barrier_certificate2.m │ ├── create_si_barrier_certificate.m │ ├── create_uni_barrier_certificate.m │ ├── create_uni_barrier_certificate_with_obstacles.m │ ├── create_si_barrier_certificate_with_boundary.m │ ├── create_uni_barrier_certificate2.m │ └── create_uni_barrier_certificate_with_boundary.m ├── Robotarium.m └── ARobotarium.m ├── tests ├── Results.m └── Mainfunction.m ├── start.m ├── proximite.m ├── Main.m └── Robot.m /utilities/OUT.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/OUT.mat -------------------------------------------------------------------------------- /tests/Results.m: -------------------------------------------------------------------------------- 1 | parfor i = 1:10 2 | time = Mainfunction; 3 | disp(time); 4 | A(i) = time; 5 | end 6 | M = mean(A); 7 | disp('moyenne ='); 8 | disp(M); -------------------------------------------------------------------------------- /utilities/graph/html/cycleGL_eq12942671300991736101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/graph/html/cycleGL_eq12942671300991736101.png -------------------------------------------------------------------------------- /utilities/graph/html/lineGL_eq12942671300991736101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/graph/html/lineGL_eq12942671300991736101.png -------------------------------------------------------------------------------- /utilities/graph/html/completeGL_eq08486233743349304198.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/graph/html/completeGL_eq08486233743349304198.png -------------------------------------------------------------------------------- /utilities/graph/html/completeGL_eq12942671300991736101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/graph/html/completeGL_eq12942671300991736101.png -------------------------------------------------------------------------------- /utilities/graph/html/randomGL_eq12942671300991736101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/graph/html/randomGL_eq12942671300991736101.png -------------------------------------------------------------------------------- /utilities/graph/html/delta_disk_neighbors_eq14324477822824332334.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/graph/html/delta_disk_neighbors_eq14324477822824332334.png -------------------------------------------------------------------------------- /utilities/graph/html/random_connectedGL_eq12942671300991736101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/graph/html/random_connectedGL_eq12942671300991736101.png -------------------------------------------------------------------------------- /utilities/graph/html/topological_neighbors_eq12941163982435923632.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/graph/html/topological_neighbors_eq12941163982435923632.png -------------------------------------------------------------------------------- /utilities/misc/html/generate_initial_conditions_eq07459738348397623860.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/misc/html/generate_initial_conditions_eq07459738348397623860.png -------------------------------------------------------------------------------- /utilities/misc/html/generate_initial_conditions_eq16033661748480904678.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/misc/html/generate_initial_conditions_eq16033661748480904678.png -------------------------------------------------------------------------------- /utilities/transformations/html/create_si_to_uni_mapping2_eq12450574036302976132.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/transformations/html/create_si_to_uni_mapping2_eq12450574036302976132.png -------------------------------------------------------------------------------- /utilities/transformations/html/create_si_to_uni_mapping_eq03927841599078721039.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/transformations/html/create_si_to_uni_mapping_eq03927841599078721039.png -------------------------------------------------------------------------------- /utilities/transformations/html/create_si_to_uni_mapping_eq06303456170493080000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/transformations/html/create_si_to_uni_mapping_eq06303456170493080000.png -------------------------------------------------------------------------------- /utilities/transformations/html/create_si_to_uni_mapping_eq12450574036302976132.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/transformations/html/create_si_to_uni_mapping_eq12450574036302976132.png -------------------------------------------------------------------------------- /utilities/transformations/html/create_si_to_uni_mapping_eq13491430810795123718.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/transformations/html/create_si_to_uni_mapping_eq13491430810795123718.png -------------------------------------------------------------------------------- /utilities/transformations/html/create_uni_to_si_mapping_eq12450574036302976132.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/transformations/html/create_uni_to_si_mapping_eq12450574036302976132.png -------------------------------------------------------------------------------- /utilities/transformations/html/create_uni_to_si_mapping_eq13491430810795123718.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iciamyplant/tournoi_fouloscopie/HEAD/utilities/transformations/html/create_uni_to_si_mapping_eq13491430810795123718.png -------------------------------------------------------------------------------- /utilities/RobotariumError.m: -------------------------------------------------------------------------------- 1 | classdef RobotariumError < double 2 | %ERRORS Used to contain Robotarium error enumeration. 3 | 4 | enumeration 5 | ExceededActuatorLimits (1) 6 | RobotsTooClose (2) 7 | RobotsOutsideBoundaries (3) 8 | end 9 | end 10 | 11 | -------------------------------------------------------------------------------- /utilities/graph/completeGL.m: -------------------------------------------------------------------------------- 1 | %% completeGL: $\mathbf{Z}^{+} \to \mathbf{Z}^{N \times N}$ 2 | % Returns a completely connected graph Laplacian 3 | %% Example Usage 4 | % L = completeGL(5); 5 | %% Implementation 6 | function [ L ] = completeGL( n ) 7 | L = n * eye(n) - ones(n,n); 8 | end 9 | 10 | -------------------------------------------------------------------------------- /utilities/graph/cycleGL.m: -------------------------------------------------------------------------------- 1 | %% cycleGL: $\mathbf{Z}^{+} \to \mathbf{Z}^{N \times N}$ 2 | % Returns a cycle graph Laplacian 3 | %% Example Usage 4 | % L = cycleGL(4) 5 | %% Implementation 6 | function [ L ] = cycleGL( n ) 7 | L = 2*eye(n) - diag(ones(1,(n-1)), 1) - diag(ones(1,(n-1)), -1); 8 | L(n, 1) = -1; 9 | L(1, n) = -1; 10 | end 11 | 12 | -------------------------------------------------------------------------------- /utilities/graph/lineGL.m: -------------------------------------------------------------------------------- 1 | %% lineGL: $\mathbf{Z}^{+} \to \mathbf{Z}^{N \times N}$ 2 | % Returns a line graph Laplacian of size n x n 3 | %% Example Usage 4 | % L = lineGL(5) 5 | %% Implementation 6 | function [ L ] = lineGL(n) 7 | L = 2*eye(n) - diag(ones(1,(n-1)), 1) - diag(ones(1, n-1), -1); 8 | L(1, 1) = 1; 9 | L(n, n) = 1; 10 | end 11 | 12 | -------------------------------------------------------------------------------- /utilities/getTargetPosition.m: -------------------------------------------------------------------------------- 1 | function [x_target,y_target] = getTargetPosition() 2 | 3 | ok = 0 ; 4 | 5 | while ok==0 6 | x_target = rand.*2.8 - 1.4 ; 7 | y_target = rand.*1.6 - 0.8 ; 8 | 9 | if abs(x_target)>0.5 || abs(y_target)>0.4 10 | ok = 1 ; 11 | end 12 | 13 | end 14 | 15 | 16 | 17 | 18 | end 19 | 20 | -------------------------------------------------------------------------------- /utilities/misc/unique_filename.m: -------------------------------------------------------------------------------- 1 | %% unique_filename 2 | % Returns a timestamped filename with *.mat appended 3 | %% Example Usage 4 | % filename = unique_filename('filename'); 5 | function [ filePath ] = unique_filename(file_name) 6 | date = datetime('now'); 7 | filePath = strcat(file_name, '_', num2str(date.Month), '_', num2str(date.Day), '_', num2str(date.Year), '_', num2str(date.Hour), '_', num2str(date.Minute), '_', num2str(date.Second), '.mat'); 8 | end 9 | 10 | -------------------------------------------------------------------------------- /utilities/graph/topological_neighbors.m: -------------------------------------------------------------------------------- 1 | %% topological_neighbors $\mathbf{R}^{N \times N} \times \mathbf{Z}^{+} \to \mathbf{Z}^{+}$ 2 | % Returns the topological neighbors of a given agent 3 | 4 | function [neighbors] = topological_neighbors(L, agent) 5 | 6 | N = size(L, 2); 7 | 8 | assert(agent <= N && agent >= 1, 'Supplied agent (%i) must be between 1 and %i', agent, N); 9 | 10 | L_agent = L(agent, :); 11 | L_agent(agent) = 0; 12 | 13 | neighbors = find(L_agent ~= 0); 14 | end 15 | 16 | -------------------------------------------------------------------------------- /start.m: -------------------------------------------------------------------------------- 1 | function retour = start(robot) 2 | %Pour démarrer la simulation 3 | % 4 | if (robot.cible_detected==0) 5 | v = [0 ; 0]; 6 | if (robot.vx==0 && robot.vy==0) 7 | if (robot.x>=0.0 && robot.y >= 0.0) 8 | v = [4 ; 4]; % haut à droite 9 | end 10 | if (robot.x<=0.0 && robot.y <= 0.0) 11 | v = [-4 ; -4]; % bas à gauche 12 | end 13 | if (robot.x<=0.0 && robot.y>=0.0) 14 | v = [-4 ; 4]; % haut à gauche 15 | end 16 | if (robot.x>=0.0 && robot.y >= 0.0) 17 | v = [4 ; -4]; % bas à droite 18 | end 19 | robot.move(v(1),v(2)); 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /utilities/graph/delta_disk_neighbors.m: -------------------------------------------------------------------------------- 1 | %% delta_disk_neighbors $\mathbf{R}^{2 \times N} \times \mathbf{Z}^{+} \times \mathbf{R} \to \mathbf{Z}^{+}$ 2 | % Returns the agents within the 2-norm of the supplied agent 3 | %% Example Usage 4 | % neighbors = delta_disk_neighbors(robot_poses, 1, 0.5); 5 | 6 | function [ neighbors ] = delta_disk_neighbors(poses, agent, delta) 7 | 8 | N = size(poses, 2); 9 | agents = 1:N; 10 | agents(agent) = []; 11 | 12 | assert(agent<=N && agent>=1, 'Supplied agent (%i) must be between 1 and %i', agent, N); 13 | 14 | within_distance = arrayfun(@(x) norm(poses(1:2, x) - poses(1:2, agent)) <= delta, agents); 15 | neighbors = agents(within_distance); 16 | end 17 | 18 | -------------------------------------------------------------------------------- /proximite.m: -------------------------------------------------------------------------------- 1 | function retour = proximite(robot, INFO) 2 | % Chaque robot suit la direction de son voisin le plus proche 3 | % 4 | courte = 0; 5 | if (INFO.nbVoisins) 6 | for i=1:INFO.nbVoisins 7 | distx = (INFO.voisins{i}.x - robot.x)^2; 8 | disty = (INFO.voisins{i}.y - robot.y)^2; 9 | dist(i) = sqrt(distx + disty); 10 | end 11 | courte = 1; 12 | for i=1:INFO.nbVoisins 13 | if (dist(i) < dist(courte)) 14 | courte = i; 15 | end 16 | end 17 | end 18 | if (courte && INFO.voisins{courte}.vx && INFO.voisins{courte}.vy) 19 | v(1) = INFO.voisins{courte}.vx; 20 | v(2) = INFO.voisins{courte}.vy; 21 | else 22 | v(1) = robot.vx; 23 | v(2) = robot.vy; 24 | end 25 | retour = v; 26 | -------------------------------------------------------------------------------- /utilities/graph/randomGL.m: -------------------------------------------------------------------------------- 1 | %% randomGL: $\mathbf{Z}^{+} \to \mathbf{Z}^{N \times N}$ 2 | % Returns a random grab laplacian with a specified number of verticies and 3 | % edges 4 | %% Example Usage 5 | % L = randomGL(5, 3); 6 | %% Implementation 7 | function [ L ] = randomGL(v, e) 8 | 9 | L = tril(ones(v, v)); 10 | 11 | %This works becuase I can't select diagonals 12 | potEdges = find(triu(L) == 0); 13 | sz = size(L); 14 | 15 | %Rest to zeros 16 | L = L - L; 17 | 18 | numEdges = min(e, length(potEdges)); 19 | edgeIndices = randperm(length(potEdges), numEdges); 20 | 21 | for index = edgeIndices 22 | 23 | [i, j] = ind2sub(sz, potEdges(index)); 24 | 25 | %Update adjacency relation 26 | L(i, j) = -1; 27 | L(j, i) = -1; 28 | 29 | %Update degree relation 30 | L(i, i) = L(i, i) + 1; 31 | L(j, j) = L(j, j) + 1; 32 | end 33 | end 34 | 35 | -------------------------------------------------------------------------------- /utilities/graph/random_connectedGL.m: -------------------------------------------------------------------------------- 1 | %% random_connectedGL: $\mathbf{Z}^{+} \to \mathbf{Z}^{N \times N}$ 2 | % Returns a random, connected GL with v verticies and (v-1) + e edges 3 | %% Example Usage 4 | % L = random_connectedGL(4, 3); 5 | %% Implementation 6 | function [ L ] = random_connectedGL(v, e) 7 | 8 | L = zeros(v, v); 9 | 10 | for i = 2:v 11 | 12 | edge = randi(i-1, 1, 1); 13 | 14 | %Update adjancency relations 15 | L(i, edge) = -1; 16 | L(edge, i) = -1; 17 | 18 | %Update node degrees 19 | L(i, i) = L(i, i) + 1; 20 | L(edge, edge) = L(edge, edge) + 1; 21 | end 22 | 23 | %This works becuase all nodes have at least 1 degree. Choose from only 24 | %upper diagonal portion 25 | potEdges = find(triu(bsxfun(@xor, L, 1)) == 1); 26 | sz = size(L); 27 | 28 | numEdges = min(e, length(potEdges)); 29 | 30 | if (numEdges <= 0) 31 | return 32 | end 33 | 34 | %Indices of randomly chosen extra edges 35 | edgeIndices = randperm(length(potEdges), numEdges); 36 | 37 | for index = edgeIndices 38 | 39 | [i, j] = ind2sub(sz, potEdges(index)); 40 | 41 | %Update adjacency relation 42 | L(i, j) = -1; 43 | L(j, i) = -1; 44 | 45 | %Update degree relation 46 | L(i, i) = L(i, i) + 1; 47 | L(j, j) = L(j, j) + 1; 48 | end 49 | end 50 | 51 | -------------------------------------------------------------------------------- /utilities/controllers/create_automatic_parking_controller.m: -------------------------------------------------------------------------------- 1 | function [ automatic_parking_controller ] = create_automatic_parking_controller(varargin) 2 | % CREATE_AUTOMATIC_PARKING_CONTROLLER Creates a controller that drive a 3 | % unicycle-modeled sytsem to a particular point and stops it (within 4 | % tolerances) 5 | % 6 | % 7 | % See also CREATE_PARKING_CONTROLLER, CREATE_IS_INITIALIZED 8 | 9 | p = inputParser; 10 | addOptional(p, 'ApproachAngleGain', 1); 11 | addOptional(p, 'DesiredAngleGain', 2.7); 12 | addOptional(p, 'RotationErrorGain', 1); 13 | addOptional(p, 'PositionError', 0.01); 14 | addOptional(p, 'RotationError', 0.25); 15 | parse(p, varargin{:}); 16 | 17 | gamma = p.Results.ApproachAngleGain; 18 | k = p.Results.DesiredAngleGain; 19 | h = p.Results.RotationErrorGain; 20 | 21 | init_checker = create_is_initialized('PositionError', p.Results.PositionError, ... 22 | 'RotationError', p.Results.RotationError); 23 | parking_controller = create_parking_controller('ApproachAngleGain', gamma, 'DesiredAngleGain', k, ... 24 | 'RotationErrorGain', h); 25 | 26 | automatic_parking_controller = @automatic_parking_controller_; 27 | 28 | function dxu = automatic_parking_controller_(states, poses) 29 | dxu = parking_controller(states, poses); 30 | [~, idxs] = init_checker(states, poses); 31 | 32 | dxu(:, idxs) = zeros(2, size(idxs, 2)); 33 | end 34 | end 35 | 36 | -------------------------------------------------------------------------------- /utilities/misc/generate_initial_conditions.m: -------------------------------------------------------------------------------- 1 | function [ poses ] = generate_initial_conditions(N, varargin) 2 | % GENERATE_INITIAL_CONDITIONS generate random poses in a circle 3 | % The default parameter values are correctly sized for the Robotarium's 4 | % physical testbed. 5 | % 6 | % GENERATE_INITIAL_CONDITIONS(5) generates 3 x 5 matrix of 7 | % random poses 8 | % 9 | % GENERATE_INITIAL_CONDITIONS(5, 'Spacing', 0.2, 'Width', 10 | % 3.2, 'Height', 2) generates 3 x 5 matrix of random poses with 11 | % spacing 0.2 m in a rectangle of 3.2 m width and 2 m height. 12 | % 13 | % Example: 14 | % poses = generate_initial_conditions(5); 15 | % 16 | % Notes: 17 | % N should be a positive integer. 18 | 19 | poses = zeros(3, N); 20 | 21 | parser = inputParser; 22 | parser.addParameter('Spacing', 0.3); 23 | parser.addParameter('Width', 3.0); 24 | parser.addParameter('Height', 1.8); 25 | parse(parser, varargin{:}); 26 | 27 | spacing = parser.Results.Spacing; 28 | width = parser.Results.Width; 29 | height = parser.Results.Height; 30 | 31 | numX = floor(width / spacing); 32 | numY = floor(height / spacing); 33 | values = randperm(numX * numY, N); 34 | 35 | for i = 1:N 36 | [x, y] = ind2sub([numX numY], values(i)); 37 | x = x*spacing - (width/2); 38 | y = y*spacing - (height/2); 39 | poses(1:2, i) = [x ; y]; 40 | end 41 | 42 | poses(3, :) = (rand(1, N)*2*pi - pi); 43 | end 44 | 45 | -------------------------------------------------------------------------------- /utilities/misc/create_is_initialized.m: -------------------------------------------------------------------------------- 1 | %% create_is_initialized 2 | % Creates a function to check for initialization. The function returns 3 | % whether all the agents have been initialized and those that have already 4 | % finished. 5 | %% Detailed Description 6 | % * PositionError - affects how close the agents are required to get to the 7 | % desired position 8 | % * RotationError - affects how close the agents are required to get to the 9 | % desired rotation 10 | %% Example Usage 11 | % initialization_checker = create_is_initialized('PositionError', 0.l, 12 | % 'RotationError', 0.01) 13 | % [all_initialized, done_idxs] = initialization_checker(robot_poses, 14 | % desired_poses) 15 | %% Implementation 16 | function [ created_is_initialized ] = create_is_initialized(varargin) 17 | 18 | parser = inputParser; 19 | parser.addParameter('PositionError', 0.01); 20 | parser.addParameter('RotationError', 0.5); 21 | parse(parser, varargin{:}); 22 | 23 | position_error = parser.Results.PositionError; 24 | rotation_error = parser.Results.RotationError; 25 | 26 | created_is_initialized = @(states, initial_conditions) is_initialized(states, initial_conditions); 27 | 28 | function [done, idxs] = is_initialized(states, initial_conditions) 29 | 30 | [M, N] = size(states); 31 | [M_ic, N_ic] = size(initial_conditions); 32 | 33 | assert(M==3, 'Dimension of states (%i) must be 3', M); 34 | assert(M_ic==3, 'Dimension of conditions (%i) must be 3', M_ic); 35 | assert(N_ic==N, 'Column dimension of states (%i) and conditions (%i) must be the same', N, N_ic); 36 | 37 | wrap = @(x) atan2(sin(x), cos(x)); 38 | f = @(x, ic) (norm(x(1:2) - ic(1:2)) <= position_error) && (abs(wrap(x(3) - ic(3))) <= rotation_error); 39 | result = arrayfun(@(x) f(states(:, x), initial_conditions(:, x)), 1:N); 40 | 41 | [done, idxs] = find(result == 1); 42 | done = (length(done) == N); 43 | end 44 | end 45 | 46 | -------------------------------------------------------------------------------- /utilities/transformations/create_si_to_uni_mapping.m: -------------------------------------------------------------------------------- 1 | %% create_si_to_uni_Mapping 2 | % Returns a mapping from single-integrator to 3 | % unicycle dynamics $\left( f: \mathbf{R}^{2 \times N} \times 4 | % \mathbf{R}^{3 \times N} \to \mathbf{R}^{2 \times N} \right)$ 5 | % and a mapping between their states $\left(f: \mathbf{R}^{3 \times N} \to 6 | % \mathbf{R}^{2 \times N} \right)$ 7 | % Using this 8 | % particular method, the single-integrator dynamics must be computed in the 9 | % single-integrator domain. 10 | %% Detailed Description 11 | % * ProjectionDistance - affects how far the single-integrator is projected 12 | % in front of the unicycle system. 13 | %% Example Usage 14 | % % si === single integrator 15 | % [si_to_uni_dynamics, uni_to_si_states] = create_si_to_uni_mapping('ProjectionDistance', 16 | % 0.05) 17 | % si_states = uni_to_si_states(robot_poses) 18 | % dx_si = si_algorithm(si_states) 19 | % dx_uni = si_to_uni_dynamics(dx_si, states) 20 | %% Implementation 21 | function [si_to_uni_dyn, uni_to_si_states] = create_si_to_uni_mapping(varargin) 22 | 23 | parser = inputParser; 24 | addOptional(parser, 'ProjectionDistance', 0.05); 25 | parse(parser, varargin{:}); 26 | 27 | projection_distance = parser.Results.ProjectionDistance; 28 | 29 | si_to_uni_dyn = @si_to_uni; 30 | uni_to_si_states = @uni_to_si_states_; 31 | 32 | T = [1 0; 0 1/projection_distance]; 33 | % First mapping from SI -> unicycle. Keeps the projected SI system at 34 | % a fixed distance from the unicycle model 35 | function dxu = si_to_uni(dxi, states) 36 | N = size(dxi, 2); 37 | dxu = zeros(2, N); 38 | for i = 1:N 39 | dxu(:, i) = T * [cos(states(3, i)) sin(states(3, i)); -sin(states(3, i)) cos(states(3,i))] * dxi(:, i); 40 | end 41 | end 42 | 43 | % Projects the single-integrator system a distance in front of the 44 | % unicycle system 45 | function xi = uni_to_si_states_(states) 46 | xi = states(1:2, :) + projection_distance*[cos(states(3, :)) ; sin(states(3, :))]; 47 | end 48 | end 49 | 50 | -------------------------------------------------------------------------------- /utilities/transformations/create_si_to_uni_dynamics.m: -------------------------------------------------------------------------------- 1 | %% create_si_to_uni_dynamics 2 | % Returns a mapping $\left( f: \mathbf{R}^{2 \times N} \times \mathbf{R}^{3 3 | % \times N} \to \mathbf{R}^{2 \times N} \right)$ 4 | % from single-integrator to unicycle dynamics 5 | %% Detailed Description 6 | % * LinearVelocityGain - affects the linear velocity for the unicycle 7 | % * AngularVelocityLimit - affects the upper (lower) bound for the 8 | % unicycle's angular velocity 9 | %% Example Usage 10 | % % si === single integrator 11 | % si_to_uni_dynamics = create_si_to_uni_dynamics() 12 | % dx_si = si_algorithm(si_states) 13 | % dx_uni = si_to_uni_dynamics(dx_si, states) 14 | %% Implementation 15 | function [si_to_uni_dyn] = create_si_to_uni_dynamics(varargin) 16 | 17 | parser = inputParser; 18 | addOptional(parser, 'LinearVelocityGain', 1); 19 | addOptional(parser, 'AngularVelocityLimit', pi); 20 | parse(parser, varargin{:}); 21 | 22 | lvg = parser.Results.LinearVelocityGain; 23 | avl = parser.Results.AngularVelocityLimit; 24 | 25 | si_to_uni_dyn = @si_to_uni; 26 | % A mapping from si -> uni dynamics. THis is more of a 27 | % projection-based method. Though, it's definitely similar to the 28 | % NIDs. 29 | function dxu = si_to_uni(dxi, states) 30 | 31 | [M, N] = size(dxi); 32 | [M_states, N_states] = size(states); 33 | 34 | assert(M==2, 'Column size of given SI velocities (%i) must be 2', M); 35 | assert(M_states==3, 'Column size of given poses (%i) must be 3', M_states); 36 | assert(N==N_states, 'Row sizes of SI velocities (%i) and poses (%i) must be the same', N, N_states); 37 | 38 | dxu = zeros(2, N); 39 | for i = 1:N 40 | dxu(1, i) = lvg * [cos(states(3, i)) sin(states(3, i))] * dxi(:, i); 41 | %Normalizing the output of atan2 to between -kw and kw 42 | dxu(2, i) = avl * atan2([-sin(states(3, i)) cos(states(3, i))]*dxi(:, i), ... 43 | [cos(states(3, i)) sin(states(3, i))]*dxi(:, i))/(pi/2); 44 | end 45 | end 46 | end 47 | 48 | -------------------------------------------------------------------------------- /utilities/gritsbot_patch.m: -------------------------------------------------------------------------------- 1 | function [ patch_data ] = gritsbot_patch() 2 | %GRITSBOT_PATCH This is a helper function to generate patches for the 3 | %simulated GRITSbots. YOU SHOULD NEVER HAVE TO USE THIS FUNCTION. 4 | % 5 | % PATCH_DATA = GRITSBOT_PATCH() generates a struct containing patch data 6 | % for a robot patch. 7 | 8 | % Make it facing 0 rads 9 | robot_width = 0.11; 10 | robot_height = 0.1; 11 | wheel_width = 0.02; 12 | wheel_height = 0.04; 13 | led_size = 0.01; 14 | 15 | % Helper functions to generate vertex coordinates for a centered 16 | % rectangle and a helper function to shift a rectangle. 17 | rectangle = @(w, h) [w/2 h/2 1; -w/2 h/2 1; -w/2 -h/2 1; w/2 -h/2 1]; 18 | shift = @(r, x, y) r + repmat([x, y, 0], size(r, 1), 1); 19 | 20 | % Create vertices for body, wheel, and led. 21 | body = rectangle(robot_width, robot_height); 22 | wheel = rectangle(wheel_width, wheel_height); 23 | led = rectangle(led_size, led_size); 24 | 25 | % Use pre-generated vertices and shift them around to create a robot 26 | left_wheel = shift(wheel, -(robot_width + wheel_width)/2, -robot_height/6); 27 | right_wheel = shift(wheel, (robot_width + wheel_width)/2, -robot_height/6); 28 | left_led = shift(led, robot_width/8, robot_height/2 - 2*led_size); 29 | right_led = shift(led, robot_width/4, robot_height/2 - 2*led_size); 30 | 31 | % Putting all the robot vertices together 32 | vertices = [ 33 | body ; 34 | left_wheel; 35 | right_wheel; 36 | left_led; 37 | right_led 38 | ]; 39 | 40 | % Only color the body of the robot. Everything else is black. 41 | colors = [ 42 | [238, 138, 17]/255; 43 | 0 0 0; 44 | 0 0 0; 45 | 0 0 0; 46 | 0 0 0 47 | ]; 48 | 49 | % This seems weird, but it basically tells the patch function which 50 | % vertices to connect. 51 | faces = repmat([1 2 3 4 1], 5, 1); 52 | 53 | for i = 2:5 54 | faces(i, :) = faces(i, :) + (i-1)*4; 55 | end 56 | 57 | patch_data = []; 58 | patch_data.vertices = vertices; 59 | patch_data.colors = colors; 60 | patch_data.faces = faces; 61 | end 62 | 63 | -------------------------------------------------------------------------------- /utilities/controllers/create_si_position_controller.m: -------------------------------------------------------------------------------- 1 | %% create_si_position_controller 2 | % Returns a controller ($u: \mathbf{R}^{2 \times N} \times \mathbf{R}^{2 \times N} \to \mathbf{R}^{2 \times N}$) 3 | % for a single-integrator system. 4 | %% Detailed Description 5 | % * XVelocityGain - affects the horizontal velocity of the 6 | % single integrator 7 | % * YVelocityGain - affects the vertical velocity of the single integrator 8 | %% Example Usage 9 | % si_position_controller = create_si_position_controller('XVelocityGain', 10 | % 1, 'YVelocityGain', 1); 11 | %% Implementation 12 | function [si_position_controller] = create_si_position_controller(varargin) 13 | 14 | parser = inputParser; 15 | addOptional(parser, 'XVelocityGain', 0.8); 16 | addOptional(parser, 'YVelocityGain', 0.8); 17 | addOptional(parser, 'VelocityMagnitudeLimit', 0.15); 18 | 19 | parse(parser, varargin{:}); 20 | 21 | x_vel_gain = parser.Results.XVelocityGain; 22 | y_vel_gain = parser.Results.YVelocityGain; 23 | velocity_magnitude_limit = parser.Results.VelocityMagnitudeLimit; 24 | gains = diag([x_vel_gain ; y_vel_gain]); 25 | 26 | si_position_controller = @position_si; 27 | 28 | 29 | function [ dx ] = position_si(states, poses) 30 | %POSITIONINT Position controller via single integrator dynamics 31 | 32 | % Error checking 33 | 34 | [M, N] = size(states); 35 | [M_poses, N_poses] = size(poses); 36 | 37 | assert(M == 2, 'Row size of states (%i) must be 2', M); 38 | assert(M_poses==2, 'Row size of SI poses (%i) must be 2', M_poses); 39 | assert(N==N_poses, 'Column size of states (%i) must be the same as poses (%i)', N, N_poses); 40 | 41 | dx = zeros(2, N); 42 | for i = 1:N 43 | dx(:, i) = gains*(poses(:, i) - states(:, i)); 44 | end 45 | 46 | % Normalize velocities to magnitude 47 | norms = arrayfun(@(idx) norm(dx(:, idx)), 1:N); 48 | to_normalize = norms > velocity_magnitude_limit; 49 | if(~isempty(norms(to_normalize))) 50 | dx(:, to_normalize) = velocity_magnitude_limit*dx(:, to_normalize)./norms(to_normalize); 51 | end 52 | end 53 | end 54 | 55 | -------------------------------------------------------------------------------- /utilities/controllers/create_unicycle_position_controller.m: -------------------------------------------------------------------------------- 1 | %% create_unicycle_position_controller 2 | % Returns a unicycle model position controller ($u: \mathbf{R}^{3 \times N} \times \mathbf{R}^{2 \times N} \to \mathbf{R}^{2 \times N}$) 3 | % given parameters 4 | %% Detailed Description 5 | %% 6 | % * LinearVelocityGain - a gain for the produced unicycle linear velocity 7 | % * AngularVelocityGain - a gain for the produced unicycle angular velocity 8 | %% Example Usage 9 | %% 10 | % controller = create_unicycle_position_controller('PositionErrorGain', 2) 11 | % controller = create_unicycle_position_controller('RotationErrorGain', 1, 12 | % 'PositionErrorGain, 2') 13 | %% Implementation 14 | function [ created_position_controller ] = create_unicycle_position_controller(varargin) 15 | p = inputParser; 16 | addOptional(p, 'LinearVelocityGain', 1); 17 | addOptional(p, 'AngularVelocityGain', 1); 18 | parse(p, varargin{:}) 19 | linear_velocity_gain = p.Results.LinearVelocityGain; 20 | angular_velocity_gain = p.Results.AngularVelocityGain; 21 | 22 | created_position_controller = @position_uni_clf; 23 | 24 | function [ dxu ] = position_uni_clf(states, poses) 25 | %POSITIONCLF Utilizes a Controlled Lyapunov Function (CLF) to drive a 26 | %unicycle system to a desired position 27 | % This function operates on unicycle states and desired poses and returns 28 | % a unicycle-velocity-valued vector. 29 | 30 | [M_states, N_states] = size(states); 31 | [M_poses, N_poses] = size(poses); 32 | 33 | assert(M_states == 3, 'Row size of states vector must be 3! Given size is %i', M_states); 34 | assert(M_poses == 2, 'Row size of desired poses (%i) must be 2!', M_poses); 35 | assert(N_states == N_poses, 'Row size of states vector (%i) must be row size of desired poses (%i)', N_states, N_poses); 36 | 37 | dxu = zeros(2, N_poses); 38 | 39 | for i = 1:N_poses 40 | pos_error = poses(:, i) - states(1:2, i); 41 | rot_error = atan2(pos_error(2), pos_error(1)); 42 | dist = norm(pos_error); 43 | 44 | dxu(1, i) = linear_velocity_gain*dist*cos(rot_error - states(3, i)); 45 | dxu(2, i) = angular_velocity_gain*dist*sin(rot_error - states(3, i)); 46 | end 47 | end 48 | end 49 | 50 | -------------------------------------------------------------------------------- /utilities/transformations/create_si_to_uni_dynamics_with_backwards_motion.m: -------------------------------------------------------------------------------- 1 | %% create_si_to_uni_dynamics_with_backwards_motion 2 | % Returns a mapping $\left( f: \mathbf{R}^{2 \times N} \times \mathbf{R}^{3 3 | % \times N} \to \mathbf{R}^{2 \times N} \right)$ 4 | % from single-integrator to unicycle dynamics. 5 | % 6 | % This implementation of the mapping allows for robots to drive backwards 7 | % if that direction of linear velocity requires less rotation. 8 | %% Parameter Description 9 | % * LinearVelocityGain - affects the linear velocity for the unicycle 10 | % * AngularVelocityLimit - affects the upper (lower) bound for the 11 | % unicycle's angular velocity 12 | %% Example Usage 13 | % % si === single integrator 14 | % si_to_uni_dynamics = create_si_to_uni_dynamics_with_backwards_motion() 15 | % dx_si = si_algorithm(si_states) 16 | % dx_uni = si_to_uni_dynamics(dx_si, states) 17 | %% Implementation 18 | function [si_to_uni_dyn] = create_si_to_uni_dynamics_with_backwards_motion(varargin) 19 | 20 | parser = inputParser; 21 | addOptional(parser, 'LinearVelocityGain', 1); 22 | addOptional(parser, 'AngularVelocityLimit', pi); 23 | parse(parser, varargin{:}); 24 | 25 | lvg = parser.Results.LinearVelocityGain; 26 | avl = parser.Results.AngularVelocityLimit; 27 | wrap = @(x) atan2(sin(x), cos(x)); 28 | 29 | si_to_uni_dyn = @si_to_uni; 30 | 31 | 32 | function dxu = si_to_uni(dxi, states) 33 | 34 | [M, N] = size(dxi); 35 | [M_states, N_states] = size(states); 36 | 37 | assert(M==2, 'Column size of given SI velocities (%i) must be 2', M); 38 | assert(M_states==3, 'Column size of given poses (%i) must be 3', M_states); 39 | assert(N==N_states, 'Row sizes of SI velocities (%i) and poses (%i) must be the same', N, N_states); 40 | 41 | dxu = zeros(2, N); 42 | for i = 1:N 43 | angle = wrap(atan2(dxi(2, i), dxi(1, i)) - states(3, i)); 44 | if(angle > -pi/2 && angle < pi/2) 45 | s = 1; 46 | else 47 | s = -1; 48 | end 49 | if(s < 0) 50 | states(3, i) = wrap(states(3, i) + pi); 51 | end 52 | dxu(1, i) = lvg*[cos(states(3, i)) sin(states(3, i))] * dxi(:, i); 53 | %Normalizing the output of atan2 to between -kw and kw 54 | dxu(2, i) = avl*atan2([-sin(states(3, i)) cos(states(3, i))]*dxi(:, i), ... 55 | dxu(1, i))/(pi/2); 56 | dxu(1, i) = s*dxu(1, i); 57 | end 58 | end 59 | end 60 | 61 | -------------------------------------------------------------------------------- /utilities/controllers/create_automatic_parking_controller2.m: -------------------------------------------------------------------------------- 1 | function [ automatic_parking_controller ] = create_automatic_parking_controller2(varargin) 2 | % CREATE_AUTOMATIC_PARKING_CONTROLLER Creates a controller that drive a 3 | % unicycle-modeled sytsem to a particular point and stops it (within 4 | % tolerances) 5 | % Works by driving the unicycle to within PositionError of the point then 6 | % rotating it to within RotationError of the desired rotation 7 | % 8 | % Args: 9 | % LinearVelocityGain, optional: see also 10 | % AngularVelocityLimit, optional: see also 11 | % PositionError, optional: Error tolerance for position 12 | % RotationError, optional: Error tolerance for rotation 13 | % VelocityMagnitudeLimit, optional: Limit for velocity while driving 14 | % to position 15 | % 16 | % See also CREATE_SI_TO_UNI_MAPPING3 17 | 18 | p = inputParser; 19 | addOptional(p, 'LinearVelocityGain', 0.5); 20 | addOptional(p, 'AngularVelocityLimit', pi/2); 21 | addOptional(p, 'PositionError', 0.03); 22 | addOptional(p, 'RotationError', 0.25); 23 | addOptional(p, 'VelocityMagnitudeLimit', 0.2) 24 | parse(p, varargin{:}); 25 | 26 | lin_vel_gain = p.Results.LinearVelocityGain; 27 | ang_vel_gain = p.Results.AngularVelocityLimit; 28 | vel_mag_limit = p.Results.VelocityMagnitudeLimit; 29 | pos_err = p.Results.PositionError; 30 | rot_err = p.Results.RotationError; 31 | 32 | position_controller = create_si_to_uni_dynamics('LinearVelocityGain', lin_vel_gain, ... 33 | 'AngularVelocityLimit', ang_vel_gain); 34 | 35 | automatic_parking_controller = @automatic_parking_controller_; 36 | 37 | function dxu = automatic_parking_controller_(states, poses) 38 | 39 | N = size(states, 2); 40 | dxu = zeros(2, N); 41 | 42 | for i = 1:N 43 | 44 | wrapped = poses(3, i) - states(3, i); 45 | wrapped = atan2(sin(wrapped), cos(wrapped)); 46 | 47 | dxi = poses(1:2, i) - states(1:2, i); 48 | 49 | % Normalize 50 | norm_ = norm(dxi); 51 | if(norm_ > vel_mag_limit) 52 | dxi = vel_mag_limit*dxi/norm_; 53 | end 54 | 55 | if(norm(dxi) > pos_err) 56 | dxu(:, i) = position_controller(dxi, states(:, i)); 57 | elseif(abs(wrapped) > rot_err) 58 | dxu(1, i) = 0; 59 | dxu(2, i) = wrapped; 60 | else 61 | dxu(:, i) = zeros(2, 1); 62 | end 63 | end 64 | end 65 | end 66 | 67 | 68 | -------------------------------------------------------------------------------- /utilities/controllers/create_parking_controller.m: -------------------------------------------------------------------------------- 1 | %% create_parking_controller 2 | % Returns a controller ($u: \mathbf{R}^{3 \times N} \times \mathbf{R}^{3 \times N} \to \mathbf{R}^{2 \times N}$) that will drive a unicycle-modeled agent to a pose 3 | % (i.e., position & orientation). 4 | %% Detailed Description 5 | %% 6 | % * ApproachAngleGain - affects how the unicycle approaches the desired 7 | % position 8 | % * DesiredAngleGain - affects how the unicycle approaches th desired angle 9 | % * RotataionErrorGain - affects how quickly the unicycle corrects rotation 10 | % errors 11 | %% Example Usage 12 | % parking_controller = create_parking_controller('ApproachAngleGain', 1, 13 | % DesiredAngleGain', 1, 'RotationErrorGain', 1); 14 | %% Implementation 15 | function [ parking_controller ] = create_parking_controller(varargin) 16 | 17 | p = inputParser; 18 | addOptional(p, 'ApproachAngleGain', 1); 19 | addOptional(p, 'DesiredAngleGain', 2.7); 20 | addOptional(p, 'RotationErrorGain', 1); 21 | parse(p, varargin{:}); 22 | 23 | gamma = p.Results.ApproachAngleGain; 24 | k = p.Results.DesiredAngleGain; 25 | h = p.Results.RotationErrorGain; 26 | 27 | parking_controller = @park; 28 | 29 | function [ dxu ] = park(states, poses) 30 | %PARK Drives a unicycle-based system to a desired pose 31 | % This controller employs a CLF to drive a unicycle-modeled system to 32 | % a desired pose (i.e., position AND orientation) 33 | 34 | [M_states, N_states] = size(states); 35 | [M_poses, N_poses] = size(poses); 36 | 37 | assert(M_states == 3, 'Row size of states vector must be 3! Given size is %i', M_states); 38 | assert(M_poses == 3, 'Row size of desired poses (%i) must be 2!', M_poses); 39 | assert(N_states == N_poses, 'Row size of states vector (%i) must be row size of desired poses (%i)', N_states, N_poses); 40 | 41 | R = @(x) [cos(x) -sin(x) ; sin(x) cos(x)]; 42 | 43 | N_states = size(states, 2); 44 | dxu = zeros(2, N_states); 45 | 46 | for i = 1:N_states 47 | 48 | translate = R(-poses(3, i))*(poses(1:2, i) - states(1:2, i)); 49 | e = norm(translate); 50 | theta = atan2(translate(2), translate(1)); 51 | alpha = theta - (states(3, i) - poses(3, i)); 52 | alpha = atan2(sin(alpha), cos(alpha)); 53 | 54 | ca = cos(alpha); 55 | sa = sin(alpha); 56 | 57 | dxu(1, i) = gamma * e * ca; 58 | dxu(2, i) = k*alpha + gamma*((ca*sa)/alpha)*(alpha + h*theta); 59 | end 60 | end 61 | end 62 | 63 | -------------------------------------------------------------------------------- /utilities/transformations/create_uni_to_si_mapping.m: -------------------------------------------------------------------------------- 1 | %% create_uni_to_si_mapping 2 | % Returns a mapping from unicycle to 3 | % single-integrator dynamics $\left( f: \mathbf{R}^{2 \times N} \times 4 | % \mathbf{R}^{3 \times N} \to \mathbf{R}^{2 \times N} \right)$ 5 | % and a mapping between their states $\left(f: \mathbf{R}^{3 \times N} \to 6 | % \mathbf{R}^{2 \times N} \right)$ 7 | % Using this 8 | % particular method, the single-integrator dynamics must be computed in the 9 | % single-integrator domain. 10 | %% Example Usage 11 | % % si === single integrator 12 | % [uni_to_si_dyn, si_to_uni_states] = create_uni_to_si_mapping('ProjectionDistance', 13 | % 0.05) 14 | % uni_states = si_to_uni_states(robot_poses) 15 | % dx_uni = uni_algorithm(uni_states) 16 | % dx_si = uni_to_si_dyn(dx_uni, states) 17 | %% Implementation 18 | function [uni_to_si_dyn, si_to_uni_states] = create_uni_to_si_mapping(varargin) 19 | 20 | parser = inputParser; 21 | addOptional(parser, 'ProjectionDistance', 0.05); 22 | parse(parser, varargin{:}); 23 | 24 | projection_distance = parser.Results.ProjectionDistance; 25 | 26 | uni_to_si_dyn = @uni_to_si; 27 | si_to_uni_states = @si_to_uni_states_; 28 | 29 | T = [1 0; 0 projection_distance]; 30 | % First mapping from SI -> unicycle. Keeps the projected SI system at 31 | % a fixed distance from the unicycle model 32 | function dxi = uni_to_si(dxu, states) 33 | [M, N] = size(dxu); 34 | [M_states, N_states] = size(states); 35 | 36 | % Dimensionality checks 37 | assert(M==2, 'Column size of unicycle velocity vector (%i) must be 2', M); 38 | assert(M_states==3, 'Column size of poses (%i) must be 3', M_states); 39 | assert(N==N_states', 'Row size of unicycle velocities (%i) must be the same as poses (%i)', N, N_states); 40 | 41 | dxi = zeros(2, N); 42 | for i = 1:N 43 | dxi(:, i) = [cos(states(3, i)) -sin(states(3, i)); sin(states(3, i)) cos(states(3,i))] * T * dxu(:, i); 44 | end 45 | end 46 | 47 | % Projects the single-integrator system a distance in front of the 48 | % unicycle system 49 | function xi = si_to_uni_states_(uni_states, si_states) 50 | 51 | % Dimensionality checks 52 | [M, N] = size(uni_states); 53 | [M_si, N_si] = size(si_states); 54 | 55 | assert(M==3, 'Column size of poses (%i) must be 3', M); 56 | assert(M_si==2, 'Column size of SI poses (%i) must be 2', M_si); 57 | assert(N==N_si, 'Row size of poses (%i) must be the same as SI poses (%i)', N, N_si); 58 | 59 | 60 | xi = si_states(1:2, :) - projection_distance*[cos(uni_states(3, :)) ; sin(uni_states(3, :))]; 61 | end 62 | end 63 | 64 | -------------------------------------------------------------------------------- /utilities/controllers/create_waypoint_controller.m: -------------------------------------------------------------------------------- 1 | function [ automatic_parking_controller ] = create_waypoint_controller(varargin) 2 | % CREATE_AUTOMATIC_PARKING_CONTROLLER Creates a controller that drive a 3 | % unicycle-modeled sytsem to a particular point and stops it (within 4 | % tolerances) 5 | % Works by driving the unicycle to within PositionError of the point then 6 | % rotating it to within RotationError of the desired rotation 7 | % 8 | % Args: 9 | % LinearVelocityGain, optional: see also 10 | % AngularVelocityLimit, optional: see also 11 | % PositionError, optional: Error tolerance for position 12 | % PositionEpsilon, optional: Epsilon 13 | % RotationError, optional: Error tolerance for rotation 14 | % VelocityMagnitudeLimit, optional: Limit for velocity while driving 15 | % to position 16 | 17 | p = inputParser; 18 | addOptional(p, 'LinearVelocityGain', 0.8); 19 | addOptional(p, 'AngularVelocityLimit', pi); 20 | addOptional(p, 'PositionError', 0.03); 21 | addOptional(p, 'PositionEpsilon', 0.01) 22 | addOptional(p, 'RotationError', 0.05); 23 | addOptional(p, 'VelocityMagnitudeLimit', 0.15) 24 | parse(p, varargin{:}); 25 | 26 | lin_vel_gain = p.Results.LinearVelocityGain; 27 | ang_vel_limit = p.Results.AngularVelocityLimit; 28 | vel_mag_limit = p.Results.VelocityMagnitudeLimit; 29 | pos_err = p.Results.PositionError; 30 | pos_eps = p.Results.PositionEpsilon; 31 | rot_err = p.Results.RotationError; 32 | approach_state = []; 33 | 34 | position_controller = create_si_to_uni_dynamics('LinearVelocityGain', lin_vel_gain, ... 35 | 'AngularVelocityLimit', ang_vel_limit); 36 | 37 | automatic_parking_controller = @automatic_parking_controller_; 38 | 39 | function [dxu, output_approach_state] = automatic_parking_controller_(states, poses, input_approach_state) 40 | 41 | N = size(states, 2); 42 | dxu = zeros(2, N); 43 | if nargin > 2 44 | approach_state = input_approach_state; 45 | elseif isempty(approach_state) 46 | approach_state = ones(1,N); 47 | end 48 | 49 | for i = 1:N 50 | 51 | wrapped = poses(3, i) - states(3, i); 52 | wrapped = atan2(sin(wrapped), cos(wrapped)); 53 | 54 | dxi = poses(1:2, i) - states(1:2, i); 55 | 56 | % Normalize 57 | norm_ = norm(dxi); 58 | 59 | if(norm_ > pos_err - pos_eps && approach_state(i)) 60 | if(norm_ > vel_mag_limit) 61 | dxi = vel_mag_limit*dxi/norm_; 62 | end 63 | dxu(:, i) = position_controller(dxi, states(:, i)); 64 | 65 | elseif(abs(wrapped) > rot_err) 66 | approach_state(i) = 0; 67 | if(norm(dxi) > pos_err) 68 | approach_state(i) = 1; 69 | end 70 | dxu(1, i) = 0; 71 | dxu(2, i) = 2*wrapped; 72 | else 73 | dxu(:, i) = zeros(2, 1); 74 | end 75 | end 76 | 77 | if nargout > 1 78 | output_approach_state = approach_state; 79 | end 80 | end 81 | end 82 | 83 | 84 | -------------------------------------------------------------------------------- /utilities/barrier_certificates/create_si_barrier_certificate2.m: -------------------------------------------------------------------------------- 1 | %% create_si_barrier_certificate 2 | % Returns a single-integrator barrier certificate function ($f : 3 | % \mathbf{R}^{2 \times N} \times \mathbf{R}^{2 \times N} \to \mathbf{R}^{2 4 | % \times N}$). This function takes a 2 x N, 2 x N single-integrator 5 | % velocity and state vector, respectively, and returns a single-integrator 6 | % velocity vector that does not induce collisions in the agents. 7 | %% Detailed Description 8 | %% 9 | % * BarrierGain - affects how quickly the agents can approach each other 10 | % * SafetyRadius - affects the distance the agents maintain 11 | %% 12 | % A good rule of thumb is to make SafetyRadius a bit larger than the agent 13 | % itself (0.08 m for the GRITSbot). 14 | 15 | %% Implementation 16 | function [ si_barrier_certificate ] = create_si_barrier_certificate2(varargin) 17 | 18 | parser = inputParser; 19 | parser.addParameter('SafeBarrierGain', 100) 20 | parser.addParameter('UnsafeBarrierGain', 1e6) 21 | parser.addParameter('SafetyRadius', 0.15); 22 | parse(parser, varargin{:}) 23 | opts = optimoptions(@quadprog,'Display','off'); 24 | 25 | gamma_safe = parser.Results.SafeBarrierGain; 26 | gamma_unsafe = parser.Results.UnsafeBarrierGain; 27 | safety_radius = parser.Results.SafetyRadius; 28 | 29 | si_barrier_certificate = @barrier_certificate; 30 | 31 | function [ dx ] = barrier_certificate(dxi, x) 32 | %BARRIERCERTIFICATE Wraps single-integrator dynamics in safety barrier 33 | %certificates 34 | % This function accepts single-integrator dynamics and wraps them in 35 | % barrier certificates to ensure that collisions do not occur. Note that 36 | % this algorithm bounds the magnitude of the generated output to 0.1. 37 | % 38 | % dx = BARRIERCERTIFICATE(dxi, x, safetyRadius) 39 | % dx: generated safe, single-integrator inputs 40 | % dxi: single-integrator synamics 41 | % x: States of the agents 42 | % safetyRadius: Size of the agents (or desired separation distance) 43 | 44 | N = size(dxi, 2); 45 | 46 | if(N < 2) 47 | dx = dxi; 48 | return 49 | end 50 | 51 | x = x(1:2, :); 52 | 53 | %Generate constraints for barrier certificates based on the size of 54 | %the safety radius 55 | num_constraints = nchoosek(N, 2); 56 | A = zeros(num_constraints, 2*N); 57 | b = zeros(num_constraints, 1); 58 | count = 1; 59 | for i = 1:(N-1) 60 | for j = (i+1):N 61 | h = norm(x(1:2,i)-x(1:2,j))^2-safety_radius^2; 62 | A(count, (2*i-1):(2*i)) = -2*(x(:,i)-x(:,j)); 63 | A(count, (2*j-1):(2*j)) = 2*(x(:,i)-x(:,j))'; 64 | if(h >= 0) 65 | b(count) = gamma_safe*h^3; 66 | else 67 | b(count) = gamma_unsafe*h^3; 68 | end 69 | count = count + 1; 70 | end 71 | end 72 | 73 | %Solve QP program generated earlier 74 | vhat = reshape(dxi,2*N,1); 75 | H = 2*eye(2*N); 76 | f = -2*vhat; 77 | 78 | vnew = quadprog(sparse(H), double(f), A, b, [],[], [], [], [], opts); 79 | 80 | %Set robot velocities to new velocities 81 | dx = reshape(vnew, 2, N); 82 | end 83 | end 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /utilities/barrier_certificates/create_si_barrier_certificate.m: -------------------------------------------------------------------------------- 1 | function [ si_barrier_certificate ] = create_si_barrier_certificate(varargin) 2 | % CREATE_SI_BARRIER_CERTIFICATE Creates a single-integrator barrier 3 | % certificate function to avoid collisions. 4 | % 5 | % Args: 6 | % BarrierGain, optional: Positive double 7 | % SafetyRadius, optional: Positive double 8 | % 9 | % Returns: 10 | % A barrier certificate function (2xN, 2xN) -> 2xN representing the 11 | % barrier certificate 12 | % 13 | % CREATE_SI_BARRIER_CERTIFICATE('SafetyRadius', 0.2) creates a function 14 | % from (2xN, 2xN) -> 2xN to keep robots at least 0.2 meters apart 15 | % (measured from their centers). 16 | % 17 | % CREATE_SI_BARRIER_CERTIFICATE('BarrierGain', 10e4) creates a 18 | % barrier certificate with a particular gain. The higher the gain, 19 | % the more quickly the robots can approach each other. 20 | % 21 | % Example: 22 | % bc = create_si_barrier_certificate('SafetyRadius', 0.2) 23 | % 24 | % Notes: 25 | % SafetyRadius should be a positive double 26 | % BarrierGain should be a positive double 27 | % In practice, the value for SafetyRadius should be a little more than double the 28 | % size of the robots. 29 | 30 | parser = inputParser; 31 | parser.addParameter('BarrierGain', 100); 32 | parser.addParameter('SafetyRadius', 0.15); 33 | parse(parser, varargin{:}) 34 | opts = optimoptions(@quadprog,'Display','off'); 35 | 36 | gamma = parser.Results.BarrierGain; 37 | safety_radius = parser.Results.SafetyRadius; 38 | 39 | si_barrier_certificate = @barrier_certificate; 40 | 41 | function [ dx ] = barrier_certificate(dxi, x) 42 | % BARRIERCERTIFICATE Wraps single-integrator dynamics in safety barrier 43 | % certificates 44 | % This function accepts single-integrator dynamics and wraps them in 45 | % barrier certificates to ensure that collisions do not occur. Note that 46 | % this algorithm bounds the magnitude of the generated output to 0.1. 47 | % 48 | % BARRIER_CERTIFICATE(dxi, x) modifies dxi to become collision 49 | % free. 50 | % 51 | % Example: 52 | % dxi is size 2xN 53 | % x is size 2xN 54 | % BARRIER_CERTIFICATE(dxi, x) 55 | % 56 | % Notes: 57 | % Try not to threshold outputs of this function. Rather, 58 | % threshold them before calling the barrier certificate. 59 | 60 | N = size(dxi, 2); 61 | 62 | if(N < 2) 63 | dx = dxi; 64 | return 65 | end 66 | 67 | x = x(1:2, :); 68 | 69 | %Generate constraints for barrier certificates based on the size of 70 | %the safety radius 71 | num_constraints = nchoosek(N, 2); 72 | A = zeros(num_constraints, 2*N); 73 | b = zeros(num_constraints, 1); 74 | count = 1; 75 | for i = 1:(N-1) 76 | for j = (i+1):N 77 | h = norm(x(1:2,i)-x(1:2,j))^2-safety_radius^2; 78 | A(count, (2*i-1):(2*i)) = -2*(x(:,i)-x(:,j)); 79 | A(count, (2*j-1):(2*j)) = 2*(x(:,i)-x(:,j))'; 80 | b(count) = gamma*h^3; 81 | count = count + 1; 82 | end 83 | end 84 | 85 | %Solve QP program generated earlier 86 | vhat = reshape(dxi,2*N,1); 87 | H = 2*eye(2*N); 88 | f = -2*vhat; 89 | 90 | vnew = quadprog(sparse(H), double(f), A, b, [],[], [], [], [], opts); 91 | 92 | %Set robot velocities to new velocities 93 | dx = reshape(vnew, 2, N); 94 | end 95 | end 96 | 97 | -------------------------------------------------------------------------------- /utilities/barrier_certificates/create_uni_barrier_certificate.m: -------------------------------------------------------------------------------- 1 | function [ uni_barrier_certificate ] = create_uni_barrier_certificate(varargin) 2 | % CREATE_SI_BARRIER_CERTIFICATE Creates a unicycle barrier 3 | % certificate function to avoid collisions. 4 | % 5 | % Args: 6 | % BarrierGain, optional: How quickly robots can approach eachother 7 | % SafetyRadius, optional: How far apart centers of robots should 8 | % remain 9 | % ProjectionDistance, optional: How far ahead to project a virtual 10 | % single integrator 11 | % VelocityMagnitudeLimit, optional: The maximum velocity for the 12 | % virtual single integrator 13 | % 14 | % Returns: 15 | % A barrier certificate function (2xN, 3xN) -> 2xN representing the 16 | % barrier certificate 17 | % 18 | % CREATE_UNI_BARRIER_CERTIFICATE('BarrierGain', bg) 19 | % 20 | % CREATE_UNI_BARRIER_CERTIFICATE('SafetyRadius', sr) 21 | % 22 | % CREATE_UNI_BARRIER_CERTIFICATE('SafetyRadius', sr, 'BarrierGain', bg) 23 | % 24 | % Example: 25 | % bc = create_si_barrier_certificate('SafetyRadius', 0.2) 26 | % 27 | % Notes: 28 | % SafetyRadius should be a positive double 29 | % BarrierGain should be a positive double 30 | % In practice, the value for SafetyRadius should be a little more than double the 31 | % size of the robots. 32 | 33 | parser = inputParser; 34 | addOptional(parser, 'BarrierGain', 100); 35 | addOptional(parser, 'SafetyRadius', 0.15); 36 | addOptional(parser, 'ProjectionDistance', 0.05); 37 | addOptional(parser, 'VelocityMagnitudeLimit', 0.2); 38 | parse(parser, varargin{:}) 39 | 40 | opts = optimoptions(@quadprog,'Display','off'); 41 | gamma = parser.Results.BarrierGain; 42 | safety_radius = parser.Results.SafetyRadius; 43 | projection_distance = parser.Results.ProjectionDistance; 44 | velocity_magnitude_limit = parser.Results.VelocityMagnitudeLimit; 45 | 46 | [si_uni_dyn, uni_si_states] = create_si_to_uni_mapping('ProjectionDistance', projection_distance); 47 | uni_si_dyn = create_uni_to_si_mapping('ProjectionDistance', projection_distance); 48 | 49 | uni_barrier_certificate = @barrier_unicycle; 50 | 51 | function [ dxu ] = barrier_unicycle(dxu, x) 52 | % BARRIER_UNICYCLE The parameterized barrier function 53 | % 54 | % Args: 55 | % dxu: 2xN vector of unicycle control inputs 56 | % x: 3xN vector of unicycle states 57 | % 58 | % Returns: 59 | % A 2xN matrix of safe unicycle control inputs 60 | % 61 | % BARRIER_UNICYCLE(dxu, x) 62 | 63 | N = size(dxu, 2); 64 | 65 | if(N < 2) 66 | return 67 | end 68 | 69 | %Shift to single integrator domain 70 | xi = uni_si_states(x); 71 | dxi = uni_si_dyn(dxu, x); 72 | 73 | % Normalize velocities 74 | norms = arrayfun(@(idx) norm(dxi(:, idx)), 1:N); 75 | to_normalize = norms > velocity_magnitude_limit; 76 | if(size(to_normalize, 2) > 0) 77 | dxi(:, to_normalize) = velocity_magnitude_limit*dxi(:, to_normalize)./norms(to_normalize); 78 | end 79 | 80 | %Generate constraints for barrier certificates based on the size of 81 | %the safety radius 82 | num_constraints = nchoosek(N, 2); 83 | A = zeros(num_constraints, 2*N); 84 | b = zeros(num_constraints, 1); 85 | count = 1; 86 | for i = 1:(N-1) 87 | for j = (i+1):N 88 | h = norm(xi(:,i)-xi(:,j))^2-(safety_radius + 2*projection_distance)^2; 89 | A(count, (2*i-1):(2*i)) = 2*(xi(:,i)-xi(:,j))'; 90 | A(count, (2*j-1):(2*j)) = -2*(xi(:,i)-xi(:,j))'; 91 | b(count) = -gamma*h^3; 92 | count = count + 1; 93 | end 94 | end 95 | 96 | A = -A; 97 | b = -b; 98 | 99 | %Solve QP program generated earlier 100 | vhat = reshape(dxi,2*N,1); 101 | H = 2*eye(2*N); 102 | f = -2*vhat; 103 | 104 | vnew = quadprog(sparse(H), double(f), A, b, [], [], [], [], [], opts); 105 | 106 | %Set robot velocities to new velocities 107 | dxu = si_uni_dyn(reshape(vnew, 2, N), x); 108 | 109 | end 110 | end 111 | 112 | -------------------------------------------------------------------------------- /utilities/barrier_certificates/create_uni_barrier_certificate_with_obstacles.m: -------------------------------------------------------------------------------- 1 | function [ uni_barrier_certificate ] = create_uni_barrier_certificate_with_obstacles(varargin) 2 | % CREATE_UNI_BARRIER_CERTIFICATE_WITH_OBSTACLES Creates a barrier certificate for a 3 | % unicycle-modeled systems 4 | % Works by projecting a virtual single-integrator system ahead of the 5 | % unicycle and applying a suitably large barrier certificate to the virtual 6 | % system. 7 | % 8 | % Args: 9 | % BarrierGain, optional: A gain for how quickly the system can 10 | % approach obstacles 11 | % SafetyRadius, optional: How far points should remain apart. 12 | % Nominal value is 1.5*robot_diameter 13 | % ProjectionDistance: How far ahead to place the virtual 14 | % single-integrator system 15 | % VelocityMagnitudeLimit: Limit for the magnitude of the virtual 16 | % single integrator 17 | % 18 | % Returns: 19 | % A barrier function (2xN, 3xN, 2xN) -> 2xN that generates safe 20 | % control inputs for unicycle-modeled systems. 21 | % 22 | % CREATE_UNI_BARRIER_CERTIFICATE_WITH_OBSTACLES('BarrierGain', 8e3) 23 | % 24 | % CREATE_UNI_BARRIER_CERTIFICATE_WITH_OBSTACLES('SafetyRadius', 0.15) 25 | % 26 | % CREATE_UNI_BARRIER_CERTIFICATE_WITH_OBSTACLES('ProjectionDistance', 27 | % 0.05) 28 | % 29 | % CREATE_UNI_BARRIER_CERTIFICATE_WITH_OBSTACLES('VelocityMagnitudeLimit', 30 | % 0.4) 31 | 32 | parser = inputParser; 33 | addOptional(parser, 'BarrierGain', 100); 34 | addOptional(parser, 'SafetyRadius', 0.15); 35 | addOptional(parser, 'ProjectionDistance', 0.05) 36 | addOptional(parser, 'VelocityMagnitudeLimit', 0.2); 37 | parse(parser, varargin{:}) 38 | 39 | opts = optimoptions(@quadprog,'Display','off'); 40 | gamma = parser.Results.BarrierGain; 41 | safety_radius = parser.Results.SafetyRadius; 42 | projection_distance = parser.Results.ProjectionDistance; 43 | velocity_magnitude_limit = parser.Results.VelocityMagnitudeLimit; 44 | 45 | [si_uni_dyn, uni_si_states] = create_si_to_uni_mapping('ProjectionDistance', projection_distance); 46 | uni_si_dyn = create_uni_to_si_mapping('ProjectionDistance', projection_distance); 47 | 48 | uni_barrier_certificate = @barrier_unicycle; 49 | 50 | function [ dxu ] = barrier_unicycle(dxu, x, obstacles) 51 | N = size(dxu, 2); 52 | 53 | %Shift to single integrator domain 54 | xi = uni_si_states(x); 55 | dxi = uni_si_dyn(dxu, x); 56 | 57 | % Normalize velocities 58 | norms = arrayfun(@(idx) norm(dxi(:, idx)), 1:N); 59 | to_normalize = norms > velocity_magnitude_limit; 60 | 61 | if(any(to_normalize == 1)) 62 | dxi(:, to_normalize) = velocity_magnitude_limit*dxi(:, to_normalize)./norms(to_normalize); 63 | end 64 | 65 | %Generate constraints for barrier certificates based on the size of 66 | %the safety radius 67 | if(N > 2) 68 | temp = nchoosek(N, 2); 69 | else 70 | temp = N - 1; 71 | end 72 | num_constraints = temp + size(obstacles, 2); 73 | A = zeros(num_constraints, 2*N); 74 | b = zeros(num_constraints, 1); 75 | count = 1; 76 | for i = 1:(N-1) 77 | for j = (i+1):N 78 | h = norm(xi(:,i)-xi(:,j))^2-(safety_radius + 2*projection_distance)^2; 79 | A(count, (2*i-1):(2*i)) = 2*(xi(:,i)-xi(:,j))'; 80 | A(count, (2*j-1):(2*j)) = -2*(xi(:,i)-xi(:,j))'; 81 | b(count) = -gamma*h^3; 82 | count = count + 1; 83 | end 84 | end 85 | 86 | % Do obstacles 87 | for i = 1:N 88 | for j = 1:size(obstacles, 2) 89 | h = norm(xi(:,i)-obstacles(:,j))^2-(safety_radius + projection_distance)^2; 90 | A(count, (2*i-1):(2*i)) = 2*(xi(:,i)-obstacles(:,j))'; 91 | b(count) = -gamma*h^3; 92 | count = count + 1; 93 | end 94 | end 95 | 96 | A = -A; 97 | b = -b; 98 | 99 | %Solve QP program generated earlier 100 | vhat = reshape(dxi,2*N,1); 101 | H = 2*eye(2*N); 102 | f = -2*vhat; 103 | 104 | vnew = quadprog(sparse(H), double(f), A, b, [], [], [], [], [], opts); 105 | 106 | %Set robot velocities to new velocities 107 | dxu = si_uni_dyn(reshape(vnew, 2, N), x); 108 | 109 | end 110 | end 111 | 112 | -------------------------------------------------------------------------------- /utilities/graph/html/completeGL.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | completeGL:

completeGL: $\mathbf{Z}^{+} \to \mathbf{Z}^{N \times N}$

Returns a completely connected graph Laplacian

Contents

Example Usage

L = completeGL(5);
70 | 

Implementation

function [ L ] = completeGL( n )
71 |     L = n * eye(n) - ones(n,n);
72 | end
73 | 
-------------------------------------------------------------------------------- /utilities/graph/html/cycleGL.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | cycleGL:

cycleGL: $\mathbf{Z}^{+} \to \mathbf{Z}^{N \times N}$

Returns a cycle graph Laplacian

Contents

Example Usage

L = cycleGL(4)
70 | 

Implementation

function [ L ] = cycleGL( n )
71 |     L = 2*eye(n) - diag(ones(1,(n-1)), 1) - diag(ones(1,(n-1)), -1);
72 |     L(n, 1) = -1;
73 |     L(1, n) = -1;
74 | end
75 | 
-------------------------------------------------------------------------------- /utilities/graph/html/lineGL.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | lineGL:

lineGL: $\mathbf{Z}^{+} \to \mathbf{Z}^{N \times N}$

Returns a line graph Laplacian of size n x n

Contents

Example Usage

L = lineGL(5)
70 | 

Implementation

function [ L ] = lineGL(n)
71 |     L = 2*eye(n) - diag(ones(1,(n-1)), 1) - diag(ones(1, n-1), -1);
72 |     L(1, 1) = 1;
73 |     L(n, n) = 1;
74 | end
75 | 
-------------------------------------------------------------------------------- /utilities/barrier_certificates/create_si_barrier_certificate_with_boundary.m: -------------------------------------------------------------------------------- 1 | function [ si_barrier_certificate ] = create_si_barrier_certificate_with_boundary(varargin) 2 | % CREATE_SI_BARRIER_CERTIFICATE Creates a single-integrator barrier 3 | % certificate function to avoid collisions and remain inside a bounded rectangle. 4 | % 5 | % Args: 6 | % BarrierGain, optional: Positive double 7 | % SafetyRadius, optional: Positive double 8 | % MagnitudeLimit, optional: Positive double 9 | % BoundaryPoints, optional: Array of rectangle limits [xmin, xmax, ymin, ymax] 10 | % 11 | % Returns: 12 | % A barrier certificate function (2xN, 2xN) -> 2xN representing the 13 | % barrier certificate 14 | % 15 | % CREATE_SI_BARRIER_CERTIFICATE('SafetyRadius', 0.2) creates a function 16 | % from (2xN, 2xN) -> 2xN to keep robots at least 0.2 meters apart 17 | % (measured from their centers). 18 | % 19 | % CREATE_SI_BARRIER_CERTIFICATE('BarrierGain', 10e4) creates a 20 | % barrier certificate with a particular gain. The higher the gain, 21 | % the more quickly the robots can approach each other. 22 | % 23 | % Example: 24 | % bc = create_si_barrier_certificate('SafetyRadius', 0.2) 25 | % 26 | % Notes: 27 | % SafetyRadius should be a positive double 28 | % BarrierGain should be a positive double 29 | % In practice, the value for SafetyRadius should be a little more than double the 30 | % size of the robots. 31 | 32 | parser = inputParser; 33 | parser.addParameter('BarrierGain', 100); 34 | parser.addParameter('SafetyRadius', 0.15); 35 | parser.addParameter('MagnitudeLimit', 0.2); 36 | parser.addParameter('BoundaryPoints', [-1.6, 1.6, -1.0, 1.0]) 37 | parse(parser, varargin{:}) 38 | opts = optimoptions(@quadprog,'Display','off'); 39 | 40 | gamma = parser.Results.BarrierGain; 41 | safety_radius = parser.Results.SafetyRadius; 42 | magnitude_limit = parser.Results.MagnitudeLimit; 43 | boundary_points = parser.Results.BoundaryPoints; 44 | 45 | si_barrier_certificate = @barrier_certificate; 46 | 47 | function [ dx ] = barrier_certificate(dxi, x) 48 | % BARRIERCERTIFICATE Wraps single-integrator dynamics in safety barrier 49 | % certificates 50 | % This function accepts single-integrator dynamics and wraps them in 51 | % barrier certificates to ensure that collisions do not occur. Note that 52 | % this algorithm bounds the magnitude of the generated output to 0.1. 53 | % 54 | % BARRIER_CERTIFICATE(dxi, x) modifies dxi to become collision 55 | % free. 56 | % 57 | % Example: 58 | % dxi is size 2xN 59 | % x is size 2xN 60 | % BARRIER_CERTIFICATE(dxi, x) 61 | % 62 | % Notes: 63 | % Try not to threshold outputs of this function. Rather, 64 | % threshold them before calling the barrier certificate. 65 | 66 | N = size(dxi, 2); 67 | 68 | if(N < 2) 69 | dx = dxi; 70 | return 71 | end 72 | 73 | x = x(1:2, :); 74 | 75 | %Generate constraints for barrier certificates based on the size of 76 | %the safety radius 77 | num_constraints = nchoosek(N, 2); 78 | A = zeros(num_constraints, 2*N); 79 | b = zeros(num_constraints, 1); 80 | count = 1; 81 | for i = 1:(N-1) 82 | for j = (i+1):N 83 | h = norm(x(1:2,i)-x(1:2,j))^2-safety_radius^2; 84 | A(count, (2*i-1):(2*i)) = -2*(x(:,i)-x(:,j)); 85 | A(count, (2*j-1):(2*j)) = 2*(x(:,i)-x(:,j))'; 86 | b(count) = gamma*h^3; 87 | count = count + 1; 88 | end 89 | end 90 | 91 | for k = 1:N 92 | %Pos Y 93 | A(count, (2*k-1):(2*k)) = [0,1]; 94 | b(count) = 0.4*gamma*(boundary_points(4)-safety_radius/2 - x(2,k))^3; 95 | count = count + 1; 96 | 97 | %Neg Y 98 | A(count, (2*k-1):(2*k)) = [0,-1]; 99 | b(count) = 0.4*gamma*(-boundary_points(3)-safety_radius/2 + x(2,k))^3; 100 | count = count + 1; 101 | 102 | %Pos X 103 | A(count, (2*k-1):(2*k)) = [1,0]; 104 | b(count) = 0.4*gamma*(boundary_points(2)-safety_radius/2 - x(1,k))^3; 105 | count = count + 1; 106 | 107 | %Neg X 108 | A(count, (2*k-1):(2*k)) = [-1,0]; 109 | b(count) = 0.4*gamma*(-boundary_points(1)-safety_radius/2 + x(1,k))^3; 110 | count = count + 1; 111 | end 112 | 113 | % Threshold control inputs before QP 114 | norms = arrayfun(@(x) norm(dxi(:, x)), 1:N); 115 | threshold = magnitude_limit; 116 | to_thresh = norms > threshold; 117 | dxi(:, to_thresh) = threshold*dxi(:, to_thresh)./norms(to_thresh); 118 | 119 | %Solve QP program generated earlier 120 | vhat = reshape(dxi,2*N,1); 121 | H = 2*eye(2*N); 122 | f = -2*vhat; 123 | 124 | vnew = quadprog(sparse(H), double(f), A, b, [],[], [], [], [], opts); 125 | 126 | %Set robot velocities to new velocities 127 | dx = reshape(vnew, 2, N); 128 | end 129 | end 130 | 131 | -------------------------------------------------------------------------------- /utilities/graph/html/topological_neighbors.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | topological_neighbors

topological_neighbors $\mathbf{R}^{N \times N} \times \mathbf{Z}^{+} \to \mathbf{Z}^{+}$

Returns the topological neighbors of a given agent

function [neighbors] = topological_neighbors(L, agent)
70 | 
71 |     N = size(L, 2);
72 | 
73 |     assert(agent<=N && agent>=1, 'Supplied agent (%i) must be between 1 and %i', agent, N);
74 | 
75 |     L_agent = L(agent, :);
76 |     L_agent(agent) = 0;
77 | 
78 |     neighbors = find(L_agent ~= 0);
79 | end
80 | 
-------------------------------------------------------------------------------- /utilities/misc/html/unique_filename.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | unique_filename

unique_filename

Returns a timestamped filename with *.mat appended

Contents

Example Usage

filename = unique_filename('filename');
70 | 
function [ filePath ] = unique_filename(file_name)
71 |     date = datetime('now');
72 |     filePath = strcat(file_name, '_', num2str(date.Month), '_', num2str(date.Day), '_', num2str(date.Year), '_', num2str(date.Hour), '_', num2str(date.Minute), '_', num2str(date.Second), '.mat');
73 | end
74 | 
-------------------------------------------------------------------------------- /utilities/graph/html/delta_disk_neighbors.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | delta_disk_neighbors

delta_disk_neighbors $\mathbf{R}^{2 \times N} \times \mathbf{Z}^{+} \times \mathbf{R} \to \mathbf{Z}^{+}$

Returns the agents within the 2-norm of the supplied agent

Contents

Example Usage

neighbors = delta_disk_neighbors(robot_poses, 1, 0.5);
 70 | 
function [ neighbors ] = delta_disk_neighbors(poses, agent, delta)
 71 | 
 72 |     N = size(poses, 2);
 73 |     agents = 1:N;
 74 |     agents(agent) = [];
 75 | 
 76 |     assert(agent<=N && agent>=1, 'Supplied agent (%i) must be between 1 and %i', agent, N);
 77 | 
 78 |     within_distance = arrayfun(@(x) norm(poses(1:2, x) - poses(1:2, agent)) <= delta, agents);
 79 |     neighbors = agents(find(within_distance));
 80 | end
 81 | 
-------------------------------------------------------------------------------- /utilities/misc/html/generate_initial_conditions.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | generate_initial_conditions:

generate_initial_conditions: $\mathbf{Z}^{+} \to \mathbf{R}^{3 \times N}$

Returns a set of random poses distributed in the Robotarium workspace

Contents

Example Usage

initial_conditions = generate_initial_conditions(4);
 70 | 

Implementation

function [ poses ] = generate_initial_conditions(N)
 71 | 
 72 |     poses = zeros(3, N);
 73 | 
 74 |     safetyRadius = 0.2;
 75 |     width = 1.15;
 76 |     height = 0.65;
 77 | 
 78 |     numX = floor(width / safetyRadius);
 79 |     numY = floor(height / safetyRadius);
 80 |     values = randperm(numX * numY, N);
 81 | 
 82 |     for i = 1:N
 83 |        [x, y] = ind2sub([numX numY], values(i));
 84 |        x = x*safetyRadius - (width/2);
 85 |        y = y*safetyRadius - (height/2);
 86 |        poses(1:2, i) = [x ; y];
 87 |     end
 88 | 
 89 |     poses(3, :) = (rand(1, N)*2*pi - pi);
 90 | end
 91 | 
-------------------------------------------------------------------------------- /utilities/graph/html/randomGL.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | randomGL:

randomGL: $\mathbf{Z}^{+} \to \mathbf{Z}^{N \times N}$

Returns a random grab laplacian with a specified number of verticies and edges

Contents

Example Usage

L = randomGL(5, 3);
 70 | 

Implementation

function [ L ] = randomGL(v, e)
 71 | 
 72 |     L = tril(ones(v, v));
 73 | 
 74 |     %This works becuase I can't select diagonals
 75 |     potEdges = find(triu(L) == 0);
 76 |     sz = size(L);
 77 | 
 78 |     %Rest to zeros
 79 |     L = L - L;
 80 | 
 81 |     numEdges = min(e, length(potEdges));
 82 |     edgeIndices = randperm(length(potEdges), numEdges);
 83 | 
 84 |     for index = edgeIndices
 85 | 
 86 |        [i, j] = ind2sub(sz, potEdges(index));
 87 | 
 88 |         %Update adjacency relation
 89 |         L(i, j) = -1;
 90 |         L(j, i) = -1;
 91 | 
 92 |         %Update degree relation
 93 |         L(i, i) = L(i, i) + 1;
 94 |         L(j, j) = L(j, j) + 1;
 95 |     end
 96 | end
 97 | 
-------------------------------------------------------------------------------- /utilities/barrier_certificates/create_uni_barrier_certificate2.m: -------------------------------------------------------------------------------- 1 | function [ uni_barrier_certificate ] = create_uni_barrier_certificate2(varargin) 2 | % CREATE_SI_BARRIER_CERTIFICATE Creates a unicycle barrier 3 | % certificate function to avoid collisions. 4 | % 5 | % Args: 6 | % BarrierGain, optional: How quickly robots can approach eachother 7 | % SafetyRadius, optional: How far apart centers of robots should 8 | % remain 9 | % ProjectionDistance, optional: How far ahead to project a virtual 10 | % single integrator 11 | % VelocityMagnitudeLimit, optional: The maximum velocity for the 12 | % virtual single integrator 13 | % 14 | % Returns: 15 | % A barrier certificate function (2xN, 3xN) -> 2xN representing the 16 | % barrier certificate 17 | % 18 | % CREATE_UNI_BARRIER_CERTIFICATE('BarrierGain', bg) 19 | % 20 | % CREATE_UNI_BARRIER_CERTIFICATE('SafetyRadius', sr) 21 | % 22 | % CREATE_UNI_BARRIER_CERTIFICATE('SafetyRadius', sr, 'BarrierGain', bg) 23 | % 24 | % Example: 25 | % bc = create_si_barrier_certificate('SafetyRadius', 0.2) 26 | % 27 | % Notes: 28 | % SafetyRadius should be a positive double 29 | % BarrierGain should be a positive double 30 | % In practice, the value for SafetyRadius should be a little more than double the 31 | % size of the robots. 32 | parser = inputParser; 33 | addOptional(parser, 'BarrierGain', 150); 34 | addOptional(parser, 'SafetyRadius', 0.12); 35 | addOptional(parser, 'ProjectionDistance', 0.05); 36 | addOptional(parser, 'BaseLength', 0.105); 37 | addOptional(parser, 'WheelRadius', 0.016); 38 | addOptional(parser, 'WheelVelocityLimit', 12.5); 39 | addOptional(parser, 'Disturbance', 5); 40 | addOptional(parser, 'MaxNumRobots', 30); 41 | addOptional(parser, 'MaxNumObstacles', 100); 42 | parse(parser, varargin{:}) 43 | 44 | opts = optimoptions(@quadprog,'Display', 'off', 'TolFun', 1e-5, 'TolCon', 1e-4); 45 | gamma = parser.Results.BarrierGain; 46 | safety_radius = parser.Results.SafetyRadius; 47 | projection_distance = parser.Results.ProjectionDistance; 48 | wheel_radius = parser.Results.WheelRadius; 49 | base_length = parser.Results.BaseLength; 50 | wheel_vel_limit = parser.Results.WheelVelocityLimit; 51 | d = parser.Results.Disturbance; 52 | max_num_robots = parser.Results.MaxNumRobots; 53 | max_num_obstacles = parser.Results.MaxNumObstacles; 54 | 55 | D = [wheel_radius/2, wheel_radius/2; -wheel_radius/base_length, wheel_radius/base_length]; 56 | L = [1,0;0,projection_distance] * D; 57 | disturb = [-d,-d,d,d;-d,d,d,-d]; 58 | num_disturbs = size(disturb, 2); 59 | % 60 | % max_num_constraints = (num_disturbs^2)*nchoosek(max_num_robots, 2) + max_num_robots*max_num_obstacles*num_disturbs + max_num_robots; 61 | 62 | max_num_constraints = nchoosek(max_num_robots, 2) + max_num_robots*max_num_obstacles; %+ max_num_robots; 63 | A = zeros(max_num_constraints, 2*max_num_robots); 64 | b = zeros(max_num_constraints, 1); 65 | 66 | Os = zeros(2,max_num_robots); 67 | ps = zeros(2,max_num_robots); 68 | Ms = zeros(2,2*max_num_robots); 69 | 70 | 71 | 72 | uni_barrier_certificate = @barrier_unicycle; 73 | 74 | 75 | function [ dxu, ret ] = barrier_unicycle(dxu, x, obstacles) 76 | % BARRIER_UNICYCLE The parameterized barrier function 77 | % 78 | % Args: 79 | % dxu: 2xN vector of unicycle control inputs 80 | % x: 3xN vector of unicycle states 81 | % obstacles: Optional 2xN vector of obtacle points. 82 | % 83 | % Returns: 84 | % A 2xN matrix of safe unicycle control inputs 85 | % 86 | % BARRIER_UNICYCLE(dxu, x) 87 | 88 | if nargin < 3 89 | obstacles = []; 90 | end 91 | 92 | num_robots = size(dxu, 2); 93 | num_obstacles = size(obstacles, 2); 94 | 95 | if(num_robots < 2) 96 | temp = 0; 97 | else 98 | temp = nchoosek(num_robots, 2); 99 | end 100 | 101 | %Generate constraints for barrier certificates based on the size of 102 | %the safety radius 103 | % num_constraints = (num_disturbs^2)*temp + num_robots*num_obstacles*num_disturbs + num_robots; 104 | num_constraints = temp + num_robots*num_obstacles; 105 | A(1:num_constraints, 1:2*num_robots) = 0; 106 | Os(1,1:num_robots) = cos(x(3, :)); 107 | Os(2,1:num_robots) = sin(x(3, :)); 108 | ps(:,1:num_robots) = x(1:2, :) + projection_distance*Os(:,1:num_robots); 109 | Ms(1,1:2:2*num_robots) = Os(1,1:num_robots); 110 | Ms(1,2:2:2*num_robots) = -projection_distance*Os(2,1:num_robots); 111 | Ms(2,2:2:2*num_robots) = projection_distance*Os(1,1:num_robots); 112 | Ms(2,1:2:2*num_robots) = Os(2,1:num_robots); 113 | ret = zeros(1, temp); 114 | 115 | count = 1; 116 | for i = 1:(num_robots-1) 117 | for j = (i+1):num_robots 118 | diff = ps(:, i) - ps(:, j); 119 | hs = sum(diff.^2,1) - safety_radius^2; 120 | 121 | h_dot_i = 2*(diff)'*Ms(:,2*i-1:2*i)*D; 122 | h_dot_j = -2*(diff)'*Ms(:,2*j-1:2*j)*D; 123 | A(count, (2*i-1):(2*i)) = h_dot_i; 124 | A(count, (2*j-1):(2*j)) = h_dot_j; 125 | b(count) = -gamma*hs.^3 - min(h_dot_i*disturb) - min(h_dot_j*disturb); %repmat(h_i_disturbs, num_disturbs, 1) + repelem(h_j_disturbs, num_disturbs, 1); 126 | ret(count) = hs; 127 | 128 | count = count + 1; 129 | end 130 | end 131 | 132 | if ~isempty(obstacles) 133 | % Do obstacles 134 | for i = 1:num_robots 135 | diffs = (ps(:, i) - obstacles)'; 136 | h = sum(diffs.^2, 2) - safety_radius^2; 137 | h_dot_i = 2*diffs*Ms(:,2*i-1:2*i)*D; 138 | A(count:count+num_obstacles-1,(2*i-1):(2*i)) = h_dot_i; 139 | b(count:count+num_obstacles-1) = -gamma*h.^3 - min(h_dot_i*disturb, [], 2); 140 | count = count + num_obstacles; 141 | end 142 | end 143 | 144 | 145 | %Solve QP program generated earlier 146 | L_all = kron(eye(num_robots), L); 147 | dxu = D \ dxu; % Convert user input to differential drive 148 | %dxu(1:2,1:4) 149 | vhat = reshape(dxu,2*num_robots,1); 150 | %disp('vhat') 151 | %vhat(1:4) 152 | H = 2*(L_all')*L_all; 153 | f = -2*vhat'*(L_all')*L_all; 154 | vnew = quadprog(H, double(f), -A(1:num_constraints,1:2*num_robots), -b(1:num_constraints), [], [], -wheel_vel_limit*ones(2*num_robots,1), wheel_vel_limit*ones(2*num_robots,1), [], opts); 155 | %disp('f') 156 | %f(1:4) 157 | %Set robot velocities to new velocities 158 | dxu = reshape(vnew, 2, num_robots); 159 | dxu = D*dxu; 160 | 161 | end 162 | end 163 | 164 | -------------------------------------------------------------------------------- /utilities/Robotarium.m: -------------------------------------------------------------------------------- 1 | classdef Robotarium < ARobotarium 2 | % Robotarium This object represents your communications with the 3 | % GRITSbots. 4 | % 5 | % THIS CLASS SHOULD NEVER BE MODIFIED 6 | 7 | properties (GetAccess = private, SetAccess = private) 8 | checked_poses_already = false % Whether GET_POSES has been checked this iteration 9 | called_step_already = true % Whether STEP has been called this iteration 10 | 11 | iteration = 0; % How many times STEP has been called 12 | errors = {}; % Accumulated errors for the simulation 13 | end 14 | 15 | methods 16 | function this = Robotarium(varargin) 17 | % ROBOTARIUM Initializes the object. 18 | % 19 | % ROBOTARIUM('NumberOfRobots', 4) creates a ROBOTARIUM 20 | % object with 4 robots. 21 | % 22 | % ROBOTARIUM('NumberOfRobots', 1, 'ShowFigure', false) 23 | % creates a ROBOTARIUM object with 1 robot and shows no 24 | % figure. 25 | % 26 | % Example: 27 | % r = Robotarium('NumberOfRobots', 10, 'ShowFigure', 28 | % true) 29 | % 30 | % Notes: 31 | % The option NumberOfRobots should be a positive integer. 32 | % The option ShowFigure should be a boolean value. 33 | % The option InitialConditions should be a 3 x 34 | % NumberOfRobots matrix of initial poses. 35 | 36 | parser = inputParser; 37 | 38 | parser.addParameter('NumberOfRobots', -1); 39 | parser.addParameter('ShowFigure', true); 40 | parser.addParameter('FigureHandle', []); 41 | parser.addParameter('InitialConditions', []); 42 | 43 | parse(parser, varargin{:}) 44 | 45 | % The input will be validated by ARobotarium 46 | this = this@ARobotarium(parser.Results.NumberOfRobots, ... 47 | parser.Results.ShowFigure, parser.Results.FigureHandle); 48 | 49 | initial_conditions = parser.Results.InitialConditions; 50 | 51 | if(isempty(initial_conditions)) 52 | initial_conditions = generate_initial_conditions(this.number_of_robots, ... 53 | 'Spacing', 1.5*this.robot_diameter, ... 54 | 'Width', this.boundaries(2)-this.boundaries(1)-this.robot_diameter, ... 55 | 'Height', this.boundaries(4)-this.boundaries(3))-this.robot_diameter; 56 | end 57 | 58 | assert(all(size(initial_conditions) == [3, this.number_of_robots]), 'Initial conditions must be 3 x %i', this.number_of_robots); 59 | 60 | % Call initialize during initialization 61 | this.initialize(initial_conditions); 62 | end 63 | 64 | function poses = get_poses(this) 65 | % GET_POSES Returns the current poses of the robots 66 | % 67 | % GET_POSES() returns a 3 x NUMBER_OF_ROBOTS matrix of poses 68 | % 69 | % Example: 70 | % x = this.get_poses() 71 | % 72 | % Notes: 73 | % This function should only be called once per call of 74 | % STEP 75 | 76 | assert(~this.checked_poses_already, 'Can only call get_poses() once per call of step()!'); 77 | 78 | poses = this.poses; 79 | 80 | %Make sure it's only called once per iteration 81 | this.checked_poses_already = true; 82 | this.called_step_already = false; 83 | end 84 | 85 | function initialize(this, initial_conditions) 86 | this.poses = initial_conditions; 87 | end 88 | 89 | function step(this) 90 | % STEP Steps the simulation, updating poses of robots and 91 | % checking for errors 92 | % 93 | % STEP() 94 | % 95 | % Example: 96 | % object.step() 97 | % 98 | % Notes: 99 | % Should be called everytime GET_POSES is called 100 | 101 | assert(~this.called_step_already, 'Make sure you call get_poses before calling step!'); 102 | 103 | %Vectorize update to states 104 | i = 1:this.number_of_robots; 105 | 106 | % Validate before thresholding velocities 107 | es = this.validate(); 108 | this.errors = [this.errors, es]; 109 | this.iteration = this.iteration + 1; 110 | 111 | this.velocities = this.threshold(this.velocities); 112 | 113 | %Update velocities using unicycle dynamics 114 | temp = this.time_step.*this.velocities(1, i); 115 | this.poses(1, i) = this.poses(1, i) + temp.*cos(this.poses(3, i)); 116 | this.poses(2, i) = this.poses(2, i) + temp.*sin(this.poses(3, i)); 117 | this.poses(3, i) = this.poses(3, i) + this.time_step.*this.velocities(2, i); 118 | 119 | %Ensure that the orientations are in the right range 120 | this.poses(3, i) = atan2(sin(this.poses(3, i)), cos(this.poses(3, i))); 121 | 122 | %Allow getting of poses again 123 | this.checked_poses_already = false; 124 | this.called_step_already = true; 125 | 126 | if(this.show_figure) 127 | this.draw_robots(); 128 | uistack([this.robot_handle{:}],'top'); 129 | end 130 | end 131 | 132 | function debug(this) 133 | num_errors = 3; 134 | count = zeros(1, num_errors); 135 | for i = 1:numel(this.errors) 136 | count(this.errors{i}) = count(this.errors{i}) + 1; 137 | end 138 | 139 | fprintf('Your simulation took approximately %.2f real seconds.\n', this.iteration*this.time_step); 140 | 141 | error_strings = cell(1, num_errors); 142 | error_strings{RobotariumError.RobotsTooClose} = 'robots were too close'; 143 | error_strings{RobotariumError.RobotsOutsideBoundaries} = 'robots were outside boundaries'; 144 | error_strings{RobotariumError.ExceededActuatorLimits} = 'robots exceeded actuator limits'; 145 | 146 | fprintf('Error count for current simulation:\n'); 147 | print_error = @(x) fprintf('\t Simulation had %i %s errors.\n', count(x), error_strings{x}); 148 | print_error(RobotariumError.RobotsTooClose) 149 | print_error(RobotariumError.RobotsOutsideBoundaries); 150 | print_error(RobotariumError.ExceededActuatorLimits); 151 | 152 | if(isempty(this.errors)) 153 | fprintf('No errors in your simulation! Acceptance of experiment likely.\n') 154 | else 155 | fprintf('Please fix the noted errors in your simulation; otherwise, your experiment may be rejected.\n'); 156 | end 157 | end 158 | end 159 | end 160 | -------------------------------------------------------------------------------- /utilities/graph/html/random_connectedGL.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | random_connectedGL:

random_connectedGL: $\mathbf{Z}^{+} \to \mathbf{Z}^{N \times N}$

Returns a random, connected GL with v verticies and (v-1) + e edges

Contents

Example Usage

L = random_connectedGL(4, 3);
 70 | 

Implementation

function [ L ] = random_connectedGL(v, e)
 71 | 
 72 |     L = zeros(v, v);
 73 | 
 74 |     for i = 2:v
 75 | 
 76 |         edge = randi(i-1, 1, 1);
 77 | 
 78 |         %Update adjancency relations
 79 |         L(i, edge) = -1;
 80 |         L(edge, i) = -1;
 81 | 
 82 |         %Update node degrees
 83 |         L(i, i) = L(i, i) + 1;
 84 |         L(edge, edge) = L(edge, edge) + 1;
 85 |     end
 86 | 
 87 |     %This works becuase all nodes have at least 1 degree.  Choose from only
 88 |     %upper diagonal portion
 89 |     potEdges = find(triu(bsxfun(@xor, L, 1)) == 1);
 90 |     sz = size(L);
 91 | 
 92 |     numEdges = min(e, length(potEdges));
 93 | 
 94 |     if (numEdges <= 0)
 95 |        return
 96 |     end
 97 | 
 98 |     %Indices of randomly chosen extra edges
 99 |     edgeIndices = randperm(length(potEdges), numEdges);
100 | 
101 |     for index = edgeIndices
102 | 
103 |        [i, j] = ind2sub(sz, potEdges(index));
104 | 
105 |         %Update adjacency relation
106 |         L(i, j) = -1;
107 |         L(j, i) = -1;
108 | 
109 |         %Update degree relation
110 |         L(i, i) = L(i, i) + 1;
111 |         L(j, j) = L(j, j) + 1;
112 |     end
113 | end
114 | 
-------------------------------------------------------------------------------- /tests/Mainfunction.m: -------------------------------------------------------------------------------- 1 | function Mainfct = Mainfunction() 2 | %Ce code permet de parametrer et de lancer la simulation 3 | % Detailed explanation goes here 4 | %% Add path to subfolder 5 | addpath(genpath('utilities')); 6 | %addpath('./'); 7 | 8 | 9 | %% Set up Robotarium object 10 | 11 | N = 12; 12 | xi = [-0.45 -0.15 0.15 0.45 -0.45 -0.15 0.15 0.45 -0.45 -0.15 0.15 0.45]; 13 | yi = [0.3 0.3 0.3 0.3 0 0 0 0 -0.3 -0.3 -0.3 -0.3] ; 14 | ai = rand(1,N).*2* pi - pi ; 15 | 16 | initial_conditions = [xi ; yi ; ai ] ; 17 | 18 | r = Robotarium('NumberOfRobots', N, 'ShowFigure', true, 'InitialConditions', initial_conditions); 19 | 20 | 21 | %% PARAMETERS 22 | SPEED = r.max_linear_velocity ; % Moving speed of the robots 23 | DETECTION_RANGE = 0.05 ; % Distance to the target for detection 24 | ATTACK_RANGE = 0.5 ; % Distance to the target for attacking 25 | ATTACK_STRENGTH = 0.01 ; % Reduction of target energy for robot attacking the target 26 | 27 | PERCEPTION_RANGE = 0.35 ; % Perception range of the robots (for walls or neighbors) 28 | 29 | [x_target, y_target] = getTargetPosition(); 30 | 31 | %% VARIABLES 32 | target_energy = 100; % Experiment ends when target energy is down to 0 33 | 34 | target_detected = zeros(1,N); % Which robots have detected the target 35 | target_attacked = zeros(1,N); % Which robots are attacking the taget 36 | 37 | %% AFFICHAGE 38 | d = plot(x_target,y_target,'ro'); 39 | target_caption = text(-1.5, -1.1, sprintf('Energie de la cible : %0.1f%%', target_energy), 'FontSize', 15, 'FontWeight', 'bold', 'Color','r'); 40 | time_caption = text(-1.5, -1.2, sprintf('Temps écoulé : 0 s'), 'FontSize', 14, 'FontWeight', 'bold', 'Color','r'); 41 | uistack(target_caption, 'top'); 42 | uistack(time_caption, 'top'); 43 | uistack(d, 'bottom'); 44 | 45 | % Initialize velocity vector for agents. 46 | v = zeros(2, N); 47 | 48 | %% BARRIER CERTIFICATES 49 | uni_barrier_cert = create_uni_barrier_certificate_with_boundary(); 50 | %si_to_uni_dyn = create_si_to_uni_dynamics('LinearVelocityGain', 0.5, 'AngularVelocityLimit', pi/2); 51 | si_to_uni_dyn = create_si_to_uni_dynamics(); 52 | 53 | %% CREATE THE ROBOTS 54 | clear allRobots 55 | for i=1:N 56 | allRobots{i} = Robot(i , initial_conditions(1,i) , initial_conditions(2,i) , initial_conditions(3,i) , SPEED); 57 | end 58 | 59 | %% DATA COLLECTION 60 | expectedDuration = 5000 ; 61 | total_time = 0 ; 62 | 63 | data_target = nan(expectedDuration,2); 64 | data_X = nan(expectedDuration,N); 65 | data_Y = nan(expectedDuration,N); 66 | data_attack = nan(expectedDuration,N); 67 | data_detect = nan(expectedDuration,N); 68 | 69 | 70 | 71 | %% START OF SIMULATION 72 | iteration = 0 ; 73 | 74 | while target_energy>0 && total_time<900 75 | 76 | % Update iteration 77 | iteration = iteration + 1 ; 78 | 79 | % UPdate total time 80 | total_time = total_time + r.time_step ; 81 | 82 | % Retrieve velocity and position of the robots 83 | x = r.get_poses(); 84 | for i=1:N 85 | allRobots{i}.x = x(1,i); 86 | allRobots{i}.y = x(2,i); 87 | allRobots{i}.orientation = x(3,i); 88 | end 89 | 90 | % Distance to the target 91 | d_target = sqrt((x_target - x(1,:)).^2 + (y_target - x(2,:)).^2); 92 | 93 | % Walls detection 94 | dWalls.up = abs(1-x(2,:)); 95 | dWalls.up(dWalls.up>PERCEPTION_RANGE) = NaN ; 96 | dWalls.down = abs(-1-x(2,:)); 97 | dWalls.down(dWalls.down>PERCEPTION_RANGE) = NaN; 98 | dWalls.left = abs(-1.6-x(1,:)); 99 | dWalls.left(dWalls.left>PERCEPTION_RANGE) = NaN; 100 | dWalls.right = abs(1.6-x(1,:)); 101 | dWalls.right(dWalls.right>PERCEPTION_RANGE) = NaN; 102 | 103 | %% Behavioural algorithm for the robots 104 | 105 | % Compute distance between robots 106 | dm = squareform(pdist([x(1,:)' x(2,:)'])); 107 | 108 | for i = 1:N 109 | 110 | % Detect the neighbors within the perception range 111 | %neighbors = delta_disk_neighbors(x, i, PERCEPTION_RANGE); 112 | neighbors = find(dm(i,:)<=PERCEPTION_RANGE & dm(i,:)>0); 113 | 114 | % Collect local information for robot i 115 | clear INFO 116 | 117 | % Walls information 118 | INFO.murs.dist_haut = dWalls.up(i); 119 | INFO.murs.dist_bas = dWalls.down(i); 120 | INFO.murs.dist_gauche = dWalls.left(i); 121 | INFO.murs.dist_droite = dWalls.right(i); 122 | 123 | % Neighbors information (when applicable) 124 | INFO.nbVoisins = length(neighbors); 125 | for v = 1:length(neighbors) 126 | INFO.voisins{v} = allRobots{neighbors(v)}; 127 | end 128 | 129 | % Target information (when applicable) 130 | if (d_target(i) threshold); 181 | if ~isempty(to_thresh) 182 | dx(:, to_thresh) = threshold*dx(:, to_thresh)./norms(to_thresh); 183 | end 184 | 185 | % Transform the single-integrator dynamics to unicycle dynamics using a provided utility function 186 | dx = si_to_uni_dyn(dx, x); 187 | dx = uni_barrier_cert(dx, x); 188 | 189 | % Set velocities of agents 1:N 190 | r.set_velocities(1:N, dx); 191 | 192 | % Save data 193 | tmpAtt = zeros(1,N); 194 | tmpDet = zeros(1,N); 195 | for i=1:N 196 | if (allRobots{i}.cible_attacked==1) 197 | tmpAtt(i) = 1 ; 198 | end 199 | if (allRobots{i}.cible_detected==1) 200 | tmpDet(i) = 1 ; 201 | end 202 | end 203 | data_attack(iteration,:) = tmpAtt ; 204 | data_detect(iteration,:) = tmpDet ; 205 | data_X(iteration,:) = x(1,:); 206 | data_Y(iteration,:) = x(2,:); 207 | data_target(iteration,:) = [target_energy total_time] ; 208 | 209 | 210 | % Send the previously set velocities to the agents. This function must be called! 211 | r.step(); 212 | 213 | % Display 214 | target_caption.String = sprintf('Energie de la cible %0.1f%%', round(10.*target_energy)/10); 215 | time_caption.String = sprintf('Temps écoulé : %d s', round(iteration*r.time_step)); 216 | 217 | end 218 | 219 | % Resultat 220 | disp(['Temps total pour détruire la cible : ' num2str(round(iteration*r.time_step)) ' secondes']); 221 | Mainfct = iteration*r.time_step; 222 | 223 | % Données finales 224 | data_X = data_X(1:iteration,:); 225 | data_Y = data_Y(1:iteration,:); 226 | data_attack = data_attack(1:iteration,:); 227 | data_detect = data_detect(1:iteration,:); 228 | data_target = data_target(1:iteration,:); 229 | 230 | 231 | 232 | %r.debug(); 233 | end 234 | 235 | -------------------------------------------------------------------------------- /utilities/transformations/html/create_si_to_uni_mapping2.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | create_si_to_uni_mapping2

create_si_to_uni_mapping2

Returns a mapping $\left( f: \mathbf{R}^{2 \times N} \times \mathbf{R}^{3 \times N} \to \mathbf{R}^{2 \times N} \right)$ from single-integrator to unicycle dynamics

Contents

Detailed Description

  • LinearVelocityGain - affects the linear velocity for the unicycle
  • AngularVelocityLimit - affects the upper (lower) bound for the unicycle's angular velocity

Example Usage

% si === single integrator
 70 | si_to_uni_dynamics = create_si_to_uni_mapping2('LinearVelocityGain',
 71 | 1, 'AngularVelocityLimit', pi)
 72 | dx_si = si_algorithm(si_states)
 73 | dx_uni = si_to_uni_dynamics(dx_si, states)
 74 | 

Implementation

function [si_to_uni_dyn] = create_si_to_uni_mapping2(varargin)
 75 | 
 76 |     parser = inputParser;
 77 |     addOptional(parser, 'LinearVelocityGain', 1);
 78 |     addOptional(parser, 'AngularVelocityLimit', pi);
 79 |     parse(parser, varargin{:});
 80 | 
 81 |     lvg = parser.Results.LinearVelocityGain;
 82 |     avl = parser.Results.AngularVelocityLimit;
 83 | 
 84 |     si_to_uni_dyn = @si_to_uni;
 85 |     % A mapping from si -> uni dynamics.  THis is more of a
 86 |     % projection-based method.  Though, it's definitely similar to the
 87 |     % NIDs.
 88 |     function dxu = si_to_uni(dxi, states)
 89 |         N = size(dxi, 2);
 90 |         dxu = zeros(2, N);
 91 |         for i = 1:N
 92 |             dxu(1, i) = lvg * [cos(states(3, i)) sin(states(3, i))] * dxi(:, i);
 93 |             %Normalizing the output of atan2 to between -kw and kw
 94 |             dxu(2, i) = avl * atan2([-sin(states(3, i)) cos(states(3, i))]*dxi(:, i), ...
 95 |                                   [cos(states(3, i)) sin(states(3, i))]*dxi(:, i))/(pi/2);
 96 |         end
 97 |     end
 98 | end
 99 | 
-------------------------------------------------------------------------------- /utilities/barrier_certificates/create_uni_barrier_certificate_with_boundary.m: -------------------------------------------------------------------------------- 1 | function [ uni_barrier_certificate ] = create_uni_barrier_certificate_with_boundary(varargin) 2 | % CREATE_SI_BARRIER_CERTIFICATE Creates a unicycle barrier 3 | % certificate function to avoid collisions. 4 | % 5 | % Args: 6 | % BarrierGain, optional: How quickly robots can approach eachother 7 | % SafetyRadius, optional: How far apart centers of robots should 8 | % remain 9 | % ProjectionDistance, optional: How far ahead to project a virtual 10 | % single integrator 11 | % VelocityMagnitudeLimit, optional: The maximum velocity for the 12 | % virtual single integrator 13 | % 14 | % Returns: 15 | % A barrier certificate function (2xN, 3xN) -> 2xN representing the 16 | % barrier certificate 17 | % 18 | % CREATE_UNI_BARRIER_CERTIFICATE('BarrierGain', bg) 19 | % 20 | % CREATE_UNI_BARRIER_CERTIFICATE('SafetyRadius', sr) 21 | % 22 | % CREATE_UNI_BARRIER_CERTIFICATE('SafetyRadius', sr, 'BarrierGain', bg) 23 | % 24 | % Example: 25 | % bc = create_si_barrier_certificate('SafetyRadius', 0.2) 26 | % 27 | % Notes: 28 | % SafetyRadius should be a positive double 29 | % BarrierGain should be a positive double 30 | % In practice, the value for SafetyRadius should be a little more than double the 31 | % size of the robots. 32 | 33 | parser = inputParser; 34 | addOptional(parser, 'BarrierGain', 150); 35 | addOptional(parser, 'SafetyRadius', 0.12); 36 | addOptional(parser, 'ProjectionDistance', 0.03); 37 | addOptional(parser, 'BaseLength', 0.105); 38 | addOptional(parser, 'WheelRadius', 0.016); 39 | addOptional(parser, 'WheelVelocityLimit', 12.5); 40 | addOptional(parser, 'Disturbance', 2); 41 | addOptional(parser, 'MaxNumRobots', 30); 42 | addOptional(parser, 'MaxObstacles', 50); 43 | addOptional(parser, 'MaxNumBoundaryPoints', 4); 44 | addOptional(parser, 'BoundaryPoints', [-1.6 1.6 -1.0 1.0]); 45 | parse(parser, varargin{:}) 46 | 47 | opts = optimoptions(@quadprog,'Display', 'off', 'TolFun', 1e-5, 'TolCon', 1e-4); 48 | gamma = parser.Results.BarrierGain; 49 | safety_radius = parser.Results.SafetyRadius; 50 | projection_distance = parser.Results.ProjectionDistance; 51 | wheel_radius = parser.Results.WheelRadius; 52 | base_length = parser.Results.BaseLength; 53 | wheel_vel_limit = parser.Results.WheelVelocityLimit; 54 | d = parser.Results.Disturbance; 55 | max_num_robots = parser.Results.MaxNumRobots; 56 | max_num_boundaries = parser.Results.MaxNumBoundaryPoints; 57 | max_num_obstacles = parser.Results.MaxObstacles; 58 | boundary_points = parser.Results.BoundaryPoints; 59 | 60 | %Check given boundary points 61 | assert(length(boundary_points)==4, "Boundary points must represent a rectangle.") 62 | assert(boundary_points(2) > boundary_points(1), "Difference between x coordinates of defined rectangular boundary points must be positive.") 63 | assert(boundary_points(4) > boundary_points(3), "Difference between y coordinates of defined rectangular boundary points must be positive.") 64 | 65 | D = [wheel_radius/2, wheel_radius/2; -wheel_radius/base_length, wheel_radius/base_length]; 66 | L = [1,0;0,projection_distance] * D; 67 | disturb = [-d,-d,d,d;-d,d,d,-d]; 68 | num_disturbs = size(disturb, 2); 69 | % 70 | % max_num_constraints = (num_disturbs^2)*nchoosek(max_num_robots, 2) + max_num_robots*max_num_obstacles*num_disturbs + max_num_robots; 71 | 72 | max_num_constraints = nchoosek(max_num_robots, 2) + max_num_robots*max_num_boundaries + max_num_robots*max_num_obstacles; %+ max_num_robots; 73 | A = zeros(max_num_constraints, 2*max_num_robots); 74 | b = zeros(max_num_constraints, 1); 75 | 76 | Os = zeros(2,max_num_robots); 77 | ps = zeros(2,max_num_robots); 78 | Ms = zeros(2,2*max_num_robots); 79 | 80 | 81 | 82 | uni_barrier_certificate = @barrier_unicycle; 83 | 84 | 85 | function [ dxu, ret ] = barrier_unicycle(dxu, x, obstacles) 86 | % BARRIER_UNICYCLE The parameterized barrier function 87 | % 88 | % Args: 89 | % dxu: 2xN vector of unicycle control inputs 90 | % x: 3xN vector of unicycle states 91 | % obstacles: Optional 2xN vector of obtacle points. 92 | % 93 | % Returns: 94 | % A 2xN matrix of safe unicycle control inputs 95 | % 96 | % BARRIER_UNICYCLE(dxu, x) 97 | 98 | if nargin < 3 99 | obstacles = []; 100 | end 101 | 102 | num_robots = size(dxu, 2); 103 | num_obstacles = size(obstacles, 2); 104 | 105 | if(num_robots < 2) 106 | temp = 0; 107 | else 108 | temp = nchoosek(num_robots, 2); 109 | end 110 | 111 | %Generate constraints for barrier certificates based on the size of 112 | %the safety radius 113 | % num_constraints = (num_disturbs^2)*temp + num_robots*num_obstacles*num_disturbs + num_robots; 114 | num_constraints = temp + num_robots*num_obstacles + 4*num_robots; 115 | A(1:num_constraints, 1:2*num_robots) = 0; 116 | Os(1,1:num_robots) = cos(x(3, :)); 117 | Os(2,1:num_robots) = sin(x(3, :)); 118 | ps(:,1:num_robots) = x(1:2, :) + projection_distance*Os(:,1:num_robots); 119 | Ms(1,1:2:2*num_robots) = Os(1,1:num_robots); 120 | Ms(1,2:2:2*num_robots) = -projection_distance*Os(2,1:num_robots); 121 | Ms(2,2:2:2*num_robots) = projection_distance*Os(1,1:num_robots); 122 | Ms(2,1:2:2*num_robots) = Os(2,1:num_robots); 123 | ret = zeros(1, temp); 124 | 125 | count = 1; 126 | for i = 1:(num_robots-1) 127 | for j = (i+1):num_robots 128 | diff = ps(:, i) - ps(:, j); 129 | hs = sum(diff.^2,1) - safety_radius^2; 130 | 131 | h_dot_i = 2*(diff)'*Ms(:,2*i-1:2*i)*D; 132 | h_dot_j = -2*(diff)'*Ms(:,2*j-1:2*j)*D; 133 | A(count, (2*i-1):(2*i)) = h_dot_i; 134 | A(count, (2*j-1):(2*j)) = h_dot_j; 135 | b(count) = -gamma*hs.^3 - min(h_dot_i*disturb) - min(h_dot_j*disturb); %repmat(h_i_disturbs, num_disturbs, 1) + repelem(h_j_disturbs, num_disturbs, 1); 136 | ret(count) = hs; 137 | 138 | count = count + 1; 139 | end 140 | end 141 | 142 | if ~isempty(obstacles) 143 | % Do obstacles 144 | for i = 1:num_robots 145 | diffs = (ps(:, i) - obstacles)'; 146 | h = sum(diffs.^2, 2) - safety_radius^2; 147 | h_dot_i = 2*diffs*Ms(:,2*i-1:2*i)*D; 148 | A(count:count+num_obstacles-1,(2*i-1):(2*i)) = h_dot_i; 149 | b(count:count+num_obstacles-1) = -gamma*h.^3 - min(h_dot_i*disturb, [], 2); 150 | count = count + num_obstacles; 151 | end 152 | end 153 | 154 | for i = 1:num_robots 155 | %Pos Y 156 | A(count,(2*i-1):(2*i)) = -Ms(2,(2*i-1):(2*i))*D; 157 | b(count) = -0.4*gamma*(boundary_points(4) - safety_radius/2 - ps(2,i))^3; 158 | count = count + 1; 159 | 160 | %Neg Y 161 | A(count,(2*i-1):(2*i)) = Ms(2,(2*i-1):(2*i))*D; 162 | b(count) = -0.4*gamma*(-boundary_points(3) - safety_radius/2 + ps(2,i))^3; 163 | count = count + 1; 164 | 165 | %Pos X 166 | A(count,(2*i-1):(2*i)) = -Ms(1,(2*i-1):(2*i))*D; 167 | b(count) = -0.4*gamma*(boundary_points(2) - safety_radius/2 - ps(1,i))^3; 168 | count = count + 1; 169 | 170 | %Neg X 171 | A(count,(2*i-1):(2*i)) = Ms(1,(2*i-1):(2*i))*D; 172 | b(count) = -0.4*gamma*(-boundary_points(1) - safety_radius/2 + ps(1,i))^3; 173 | count = count + 1; 174 | end 175 | 176 | %Solve QP program generated earlier 177 | L_all = kron(eye(num_robots), L); 178 | dxu = D \ dxu; % Convert user input to differential drive 179 | %dxu(1:2,1:4) 180 | vhat = reshape(dxu,2*num_robots,1); 181 | %disp('vhat') 182 | %vhat(1:4) 183 | H = 2*(L_all')*L_all; 184 | f = -2*vhat'*(L_all')*L_all; 185 | vnew = quadprog(H, double(f), -A(1:num_constraints,1:2*num_robots), -b(1:num_constraints), [], [], -wheel_vel_limit*ones(2*num_robots,1), wheel_vel_limit*ones(2*num_robots,1), [], opts); 186 | %disp('f') 187 | %f(1:4) 188 | %Set robot velocities to new velocities 189 | dxu = reshape(vnew, 2, num_robots); 190 | dxu = D*dxu; 191 | 192 | end 193 | end 194 | 195 | -------------------------------------------------------------------------------- /Main.m: -------------------------------------------------------------------------------- 1 | %% Ce code permet de parametrer et de lancer la simulation %% 2 | % % 3 | % --------------- NE PAS MODIFIER --------------- % 4 | % % 5 | % Pour programmer les robots, utilisez Robot.m % 6 | % % 7 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | 9 | 10 | %% Add path to subfolder 11 | addpath(genpath('utilities')); 12 | %addpath('./'); 13 | 14 | 15 | %% Set up Robotarium object 16 | 17 | N = 12; 18 | xi = [-0.45 -0.15 0.15 0.45 -0.45 -0.15 0.15 0.45 -0.45 -0.15 0.15 0.45]; 19 | yi = [0.3 0.3 0.3 0.3 0 0 0 0 -0.3 -0.3 -0.3 -0.3] ; 20 | ai = rand(1,N).*2* pi - pi ; 21 | 22 | initial_conditions = [xi ; yi ; ai ] ; 23 | 24 | r = Robotarium('NumberOfRobots', N, 'ShowFigure', true, 'InitialConditions', initial_conditions); 25 | 26 | 27 | %% PARAMETERS 28 | SPEED = r.max_linear_velocity ; % Moving speed of the robots 29 | DETECTION_RANGE = 0.05 ; % Distance to the target for detection 30 | ATTACK_RANGE = 0.5 ; % Distance to the target for attacking 31 | ATTACK_STRENGTH = 0.01 ; % Reduction of target energy for robot attacking the target 32 | 33 | PERCEPTION_RANGE = 0.35 ; % Perception range of the robots (for walls or neighbors) 34 | 35 | [x_target, y_target] = getTargetPosition(); 36 | 37 | %% VARIABLES 38 | target_energy = 100; % Experiment ends when target energy is down to 0 39 | 40 | target_detected = zeros(1,N); % Which robots have detected the target 41 | target_attacked = zeros(1,N); % Which robots are attacking the taget 42 | 43 | %% AFFICHAGE 44 | d = plot(x_target,y_target,'ro'); 45 | target_caption = text(-1.5, -1.1, sprintf('Energie de la cible : %0.1f%%', target_energy), 'FontSize', 15, 'FontWeight', 'bold', 'Color','r'); 46 | time_caption = text(-1.5, -1.2, sprintf('Temps écoulé : 0 s'), 'FontSize', 14, 'FontWeight', 'bold', 'Color','r'); 47 | uistack(target_caption, 'top'); 48 | uistack(time_caption, 'top'); 49 | uistack(d, 'bottom'); 50 | 51 | % Initialize velocity vector for agents. 52 | v = zeros(2, N); 53 | 54 | %% BARRIER CERTIFICATES 55 | uni_barrier_cert = create_uni_barrier_certificate_with_boundary(); 56 | %si_to_uni_dyn = create_si_to_uni_dynamics('LinearVelocityGain', 0.5, 'AngularVelocityLimit', pi/2); 57 | si_to_uni_dyn = create_si_to_uni_dynamics(); 58 | 59 | %% CREATE THE ROBOTS 60 | clear allRobots 61 | for i=1:N 62 | allRobots{i} = Robot(i , initial_conditions(1,i) , initial_conditions(2,i) , initial_conditions(3,i) , SPEED); 63 | end 64 | 65 | %% DATA COLLECTION 66 | expectedDuration = 5000 ; 67 | total_time = 0 ; 68 | 69 | data_target = nan(expectedDuration,2); 70 | data_X = nan(expectedDuration,N); 71 | data_Y = nan(expectedDuration,N); 72 | data_attack = nan(expectedDuration,N); 73 | data_detect = nan(expectedDuration,N); 74 | 75 | 76 | 77 | %% START OF SIMULATION 78 | iteration = 0 ; 79 | 80 | while target_energy>0 && total_time<900 81 | 82 | % Update iteration 83 | iteration = iteration + 1 ; 84 | 85 | % UPdate total time 86 | total_time = total_time + r.time_step ; 87 | 88 | % Retrieve velocity and position of the robots 89 | x = r.get_poses(); 90 | for i=1:N 91 | allRobots{i}.x = x(1,i); 92 | allRobots{i}.y = x(2,i); 93 | allRobots{i}.orientation = x(3,i); 94 | end 95 | 96 | % Distance to the target 97 | d_target = sqrt((x_target - x(1,:)).^2 + (y_target - x(2,:)).^2); 98 | 99 | % Walls detection 100 | dWalls.up = abs(1-x(2,:)); 101 | dWalls.up(dWalls.up>PERCEPTION_RANGE) = NaN ; 102 | dWalls.down = abs(-1-x(2,:)); 103 | dWalls.down(dWalls.down>PERCEPTION_RANGE) = NaN; 104 | dWalls.left = abs(-1.6-x(1,:)); 105 | dWalls.left(dWalls.left>PERCEPTION_RANGE) = NaN; 106 | dWalls.right = abs(1.6-x(1,:)); 107 | dWalls.right(dWalls.right>PERCEPTION_RANGE) = NaN; 108 | 109 | %% Behavioural algorithm for the robots 110 | 111 | % Compute distance between robots 112 | dm = squareform(pdist([x(1,:)' x(2,:)'])); 113 | 114 | for i = 1:N 115 | 116 | % Detect the neighbors within the perception range 117 | %neighbors = delta_disk_neighbors(x, i, PERCEPTION_RANGE); 118 | neighbors = find(dm(i,:)<=PERCEPTION_RANGE & dm(i,:)>0); 119 | 120 | % Collect local information for robot i 121 | clear INFO 122 | 123 | % Walls information 124 | INFO.murs.dist_haut = dWalls.up(i); 125 | INFO.murs.dist_bas = dWalls.down(i); 126 | INFO.murs.dist_gauche = dWalls.left(i); 127 | INFO.murs.dist_droite = dWalls.right(i); 128 | 129 | % Neighbors information (when applicable) 130 | INFO.nbVoisins = length(neighbors); 131 | for v = 1:length(neighbors) 132 | INFO.voisins{v} = allRobots{neighbors(v)}; 133 | end 134 | 135 | % Target information (when applicable) 136 | if (d_target(i) threshold); 187 | if ~isempty(to_thresh) 188 | dx(:, to_thresh) = threshold*dx(:, to_thresh)./norms(to_thresh); 189 | end 190 | 191 | % Transform the single-integrator dynamics to unicycle dynamics using a provided utility function 192 | dx = si_to_uni_dyn(dx, x); 193 | dx = uni_barrier_cert(dx, x); 194 | 195 | % Set velocities of agents 1:N 196 | r.set_velocities(1:N, dx); 197 | 198 | % Save data 199 | tmpAtt = zeros(1,N); 200 | tmpDet = zeros(1,N); 201 | for i=1:N 202 | if (allRobots{i}.cible_attacked==1) 203 | tmpAtt(i) = 1 ; 204 | end 205 | if (allRobots{i}.cible_detected==1) 206 | tmpDet(i) = 1 ; 207 | end 208 | end 209 | data_attack(iteration,:) = tmpAtt ; 210 | data_detect(iteration,:) = tmpDet ; 211 | data_X(iteration,:) = x(1,:); 212 | data_Y(iteration,:) = x(2,:); 213 | data_target(iteration,:) = [target_energy total_time] ; 214 | 215 | 216 | % Send the previously set velocities to the agents. This function must be called! 217 | r.step(); 218 | 219 | % Display 220 | target_caption.String = sprintf('Energie de la cible %0.1f%%', round(10.*target_energy)/10); 221 | time_caption.String = sprintf('Temps écoulé : %d s', round(iteration*r.time_step)); 222 | 223 | end 224 | 225 | % Resultat 226 | disp(['Temps total pour détruire la cible : ' num2str(round(iteration*r.time_step)) ' secondes']) 227 | 228 | 229 | % Données finales 230 | data_X = data_X(1:iteration,:); 231 | data_Y = data_Y(1:iteration,:); 232 | data_attack = data_attack(1:iteration,:); 233 | data_detect = data_detect(1:iteration,:); 234 | data_target = data_target(1:iteration,:); 235 | 236 | 237 | 238 | %r.debug(); -------------------------------------------------------------------------------- /utilities/misc/html/create_is_initialized.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | create_is_initialized

create_is_initialized

Creates a function to check for initialization. The function returns whether all the agents have been initialized and those that have already finished.

Contents

Detailed Description

  • PositionError - affects how close the agents are required to get to the desired position
  • RotationError - affects how close the agents are required to get to the desired rotation

Example Usage

initialization_checker = create_is_initialized('PositionError', 0.l,
 70 | 'RotationError', 0.01)
 71 | [all_initialized, done_idxs] = initialization_checker(robot_poses,
 72 | desired_poses)
 73 | 

Implementation

function [ created_is_initialized ] = create_is_initialized(varargin)
 74 | 
 75 |     parser = inputParser;
 76 |     parser.addParameter('PositionError', 0.01);
 77 |     parser.addParameter('RotationError', 0.5);
 78 |     parse(parser, varargin{:});
 79 | 
 80 |     position_error = parser.Results.PositionError;
 81 |     rotation_error = parser.Results.RotationError;
 82 | 
 83 |     created_is_initialized = @(states, initial_conditions) is_initialized(states, initial_conditions);
 84 | 
 85 |     function [done, idxs] = is_initialized(states, initial_conditions)
 86 | 
 87 |         [M, N] = size(states);
 88 |         [M_ic, N_ic] = size(initial_conditions);
 89 | 
 90 |         assert(M==3, 'Dimension of states (%i) must be 3', M);
 91 |         assert(M_ic==3, 'Dimension of conditions (%i) must be 3', M_ic);
 92 |         assert(N_ic==N, 'Column dimension of states (%i) and conditions (%i) must be the same', N, N_ic);
 93 | 
 94 |         wrap = @(x) atan2(sin(x), cos(x));
 95 |         f = @(x, ic) (norm(x(1:2) - ic(1:2)) <= position_error) && (abs(wrap(x(3) - ic(3))) <= rotation_error);
 96 |         result = arrayfun(@(x) f(states(:, x), initial_conditions(:, x)), 1:N);
 97 | 
 98 |         [done, idxs] = find(result == 1);
 99 |         done = (length(done) == N);
100 |     end
101 | end
102 | 
-------------------------------------------------------------------------------- /utilities/transformations/html/create_uni_to_si_mapping.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | create_uni_to_si_mapping

create_uni_to_si_mapping

Returns a mapping from unicycle to single-integrator dynamics $\left( f: \mathbf{R}^{2 \times N} \times \mathbf{R}^{3 \times N} \to \mathbf{R}^{2 \times N} \right)$ and a mapping between their states $\left(f: \mathbf{R}^{3 \times N} \to \mathbf{R}^{2 \times N} \right)$ Using this particular method, the single-integrator dynamics must be computed in the single-integrator domain.

Contents

Example Usage

% si === single integrator
 70 | [uni_to_si_dyn, si_to_uni_states] = create_uni_to_si_mapping('ProjectionDistance',
 71 | 0.05)
 72 | uni_states = si_to_uni_states(robot_poses)
 73 | dx_uni = uni_algorithm(uni_states)
 74 | dx_si = uni_to_si_dyn(dx_uni, states)
 75 | 

Implementation

function [uni_to_si_dyn, si_to_uni_states] = create_uni_to_si_mapping(varargin)
 76 | 
 77 |     parser = inputParser;
 78 |     addOptional(parser, 'ProjectionDistance', 0.05);
 79 |     parse(parser, varargin{:});
 80 | 
 81 |     projection_distance = parser.Results.ProjectionDistance;
 82 | 
 83 |     uni_to_si_dyn = @uni_to_si;
 84 |     si_to_uni_states = @si_to_uni_states_;
 85 | 
 86 |     T = [1 0; 0 projection_distance];
 87 |     % First mapping from SI -> unicycle.  Keeps the projected SI system at
 88 |     % a fixed distance from the unicycle model
 89 |     function dxi = uni_to_si(dxu, states)
 90 |         N = size(dxu, 2);
 91 |         dxi = zeros(2, N);
 92 |         for i = 1:N
 93 |             dxi(:, i) = [cos(states(3, i)) -sin(states(3, i)); sin(states(3, i)) cos(states(3,i))] * T * dxu(:, i);
 94 |         end
 95 |     end
 96 | 
 97 |     % Projects the single-integrator system a distance in front of the
 98 |     % unicycle system
 99 |     function xi = si_to_uni_states_(uni_states, si_states)
100 |        xi = si_states(1:2, :) - projection_distance*[cos(uni_states(3, :)) ; sin(uni_states(3, :))];
101 |     end
102 | end
103 | 
-------------------------------------------------------------------------------- /utilities/transformations/html/create_si_to_uni_mapping.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | create_si_to_uni_Mapping

create_si_to_uni_Mapping

Returns a mapping from single-integrator to unicycle dynamics $\left( f: \mathbf{R}^{2 \times N} \times \mathbf{R}^{3 \times N} \to \mathbf{R}^{2 \times N} \right)$ and a mapping between their states $\left(f: \mathbf{R}^{3 \times N} \to \mathbf{R}^{2 \times N} \right)$ Using this particular method, the single-integrator dynamics must be computed in the single-integrator domain.

Contents

Detailed Description

  • ProjectionDistance - affects how far the single-integrator is projected in front of the unicycle system.

Example Usage

% si === single integrator
 70 | [si_to_uni_dynamics, uni_to_si_states] = create_si_to_uni_mapping('ProjectionDistance',
 71 | 0.05)
 72 | si_states = uni_to_si_states(robot_poses)
 73 | dx_si = si_algorithm(si_states)
 74 | dx_uni = si_to_uni_dynamics(dx_si, states)
 75 | 

Implementation

function [si_to_uni_dyn, uni_to_si_states] = create_si_to_uni_mapping(varargin)
 76 | 
 77 |     parser = inputParser;
 78 |     addOptional(parser, 'ProjectionDistance', 0.05);
 79 |     parse(parser, varargin{:});
 80 | 
 81 |     projection_distance = parser.Results.ProjectionDistance;
 82 | 
 83 |     si_to_uni_dyn = @si_to_uni;
 84 |     uni_to_si_states = @uni_to_si_states_;
 85 | 
 86 |     T = [1 0; 0 1/projection_distance];
 87 |     % First mapping from SI -> unicycle.  Keeps the projected SI system at
 88 |     % a fixed distance from the unicycle model
 89 |     function dxu = si_to_uni(dxi, states)
 90 |         N = size(dxi, 2);
 91 |         dxu = zeros(2, N);
 92 |         for i = 1:N
 93 |             dxu(:, i) = T * [cos(states(3, i)) sin(states(3, i)); -sin(states(3, i)) cos(states(3,i))] * dxi(:, i);
 94 |         end
 95 |     end
 96 | 
 97 |     % Projects the single-integrator system a distance in front of the
 98 |     % unicycle system
 99 |     function xi = uni_to_si_states_(states)
100 |        xi = states(1:2, :) + projection_distance*[cos(states(3, :)) ; sin(states(3, :))];
101 |     end
102 | end
103 | 
-------------------------------------------------------------------------------- /Robot.m: -------------------------------------------------------------------------------- 1 | classdef Robot < handle 2 | 3 | %% Voici un robot. 4 | % Cette classe determine le comportement du robot. 5 | % À partir d'ici, vous pourrez programmer son déplacement et 6 | % la manière dont il communique avec les autres robots. 7 | % 8 | % Lors de la simulation, il y aura 12 robots en même temps, tous identiques 9 | % (techniquement : 12 instances de cette classe). 10 | % Leur mission : trouver une cible cachée dans l'environement et la 11 | % détruire le plus vite possible. 12 | % 13 | % Vous pouvez ajouter votre code dans la fonction *update* 14 | % Vous êtes aussi libre d'ajouter des attributs, des méthodes ou des 15 | % scripts annexes si nécessaire. 16 | 17 | 18 | %% Propriétés du robot 19 | properties 20 | id % L'identifiant du robot, compris entre 1 et 12. 21 | x % La position x du robot dans l'arène 22 | y % La position y du robot dans l'arène 23 | orientation % L'orientation du robot. C'est un angle exprimé en radian où 0 indique que le robot est orienté vers l'est. 24 | 25 | % Les 4 premières propriétés se mettent à jour automatiquement. 26 | % Vous pouvez les lire si vous en avez besoin mais vous ne 27 | % pouvez pas les modifier manuellement. 28 | % Pour changer la position du robot, vous devez lui appliquer un 29 | % mouvement (vx, vy) 30 | 31 | vx % Le mouvement que le robot essaye de produire, le long de l'axe x. 32 | vy % Le mouvement que le robot essaye de produire, le long de l'axe y. 33 | 34 | % Les valeurs (vx,vy) constitue un vecteur de déplacement. Elles 35 | % sont initialisées à (0,0) ce qui signifie que le robot sera 36 | % immobile au début. Pour faire bouger votre robot, changez ces 37 | % deux valeurs à votre convenance. Par exemple pour faire bouger votre 38 | % robot vers la droite, choisissez vx = 1 et vy = 0. Pour le faire 39 | % bouger vers le bas, choisissez vx = 0 et vy = -1. 40 | % 41 | % ATTENTION : Vous ne pouvez pas déplacer le robot à une 42 | % coordonnée absolue (x,y). Le robot n'a pas de GPS, ni de 43 | % perception globale de l'environement. Vous pouvez juste lui 44 | % ordonner de se déplacer dans une certaine direction par rapport à 45 | % son emplacement actuel. 46 | % 47 | % Il est recommandé d'utiliser la fonction 'move' (voir plus bas) pour 48 | % mettre à jour vx et vy. 49 | % 50 | % Attention: le mouvement du robot est contraint par la physique du système. 51 | % Si le robot essaye d'aller contre un mur ou risque une collision avec un 52 | % autre robot, des procédures de sécurité l'en empecheront. 53 | 54 | 55 | cible_detected % 1 si le robot connait l'emplacement de la cible, 0 sinon. 56 | cible_attacked % 1 si le robot est en train d'attaquer la cible, 0 sinon. 57 | cible_x % Position x de la cible dans l'environement (initialement inconnue) 58 | cible_y % Position y de la cible dans l'environement (initialement inconnue) 59 | 60 | % Ces 4 propriétés se mettent aussi à jour automatiquement. 61 | % Vous pouvez les lire si vous en avez besoin mais vous ne 62 | % devez pas les modifier manuellement. 63 | % Dès que le robot est en contact avec la cible, la 64 | % propriété *cible_detected* passera automatiquement à 1 65 | % et les coordonnées (cible_x, cible_y) seront renseignées. Vous 66 | % n'avez pas besoin de vous en occuper. 67 | % 68 | % Une fois la cible detectée, le robot attaquera automatiquement la 69 | % cible s'il est suffisamment proche (*cible_attacked* passera à 1) 70 | % et cessera de l'attaquer s'il s'éloigne (*cible_attacked* passera 71 | % à 0). 72 | % Un robot qui a détécté la cible peut passer l'information à ses 73 | % voisins, comme nous le verront pas tard. 74 | 75 | 76 | MAX_SPEED % La vitesse de déplacement du robot. Vous ne pouvez pas modifier cette propriété. 77 | 78 | end 79 | 80 | 81 | 82 | methods 83 | 84 | function robot = Robot(id, x,y,orientation , SPEED) 85 | % Cette fonction est utilisée par le simulateur pour initialiser 86 | % le robot. Vous n'avez pas besoin de l'utiliser. 87 | 88 | robot.id = id ; 89 | robot.x = x; 90 | robot.y = y; 91 | robot.orientation = orientation ; 92 | robot.vx = 0; 93 | robot.vy = 0; 94 | robot.MAX_SPEED = SPEED ; 95 | robot.cible_detected = 0 ; 96 | robot.cible_attacked = 0 ; 97 | robot.cible_x = NaN ; 98 | robot.cible_y = NaN ; 99 | 100 | end 101 | 102 | 103 | function robot = move(robot, vx, vy) 104 | % Utilisez cette fonction pour donner une direction de 105 | % déplacement (vx, vy) à ce robot ou à un robot voisin. 106 | % Par exemple move(1,0) produira un déplacement vers la 107 | % droite. 108 | 109 | v = [vx ; vy]; 110 | if (norm(v)>0) 111 | v = robot.MAX_SPEED .* v ./ norm(v); 112 | end 113 | 114 | robot.vx = v(1) ; 115 | robot.vy = v(2) ; 116 | end 117 | 118 | 119 | function robot = set_info_cible(robot, cible_x, cible_y) 120 | % Cette fonction permet de renseigner les informations sur la 121 | % cible. Vous pouvez vous en servir pour passer l'information 122 | % à un autre robot voisin. 123 | 124 | robot.cible_detected = 1 ; 125 | 126 | robot.cible_x = cible_x ; 127 | robot.cible_y = cible_y ; 128 | end 129 | 130 | 131 | function robot = start_attack(robot) 132 | % Cette fonction est appelée automatiquement lorsque le robot 133 | % à détécté la cible et est à distance d'attaque. 134 | % Vous ne devez pas l'utiliser manuellement. 135 | robot.cible_attacked = 1 ; 136 | end 137 | 138 | 139 | function robot = stop_attack(robot) 140 | % Cett fonction est appelée automatiquement lorsque le robot 141 | % n'est plus à distance d'attaque de la cible. 142 | % Vous ne devez pas l'utiliser manuellement. 143 | robot.cible_attacked = 0 ; 144 | end 145 | 146 | 147 | %% À vous de jouer !! 148 | 149 | % Le robot va executer la fonction *update* ci-dessous 30 fois 150 | % par seconde. Vous pouvez y mettre le code que vous souhaitez. 151 | 152 | % À chaque fois que la fonction *update* est appelée, le robot 153 | % reçoit des informations sur son environement proche. 154 | % Ces informations sont contenues dans la variable INFO. 155 | 156 | % La variable INFO contient les informations suivantes : 157 | % 158 | % INFO.murs.dist_haut : La distance entre le robot et le mur 159 | % du haut de l'arène. 160 | % 161 | % INFO.murs.dist_bas : La distance entre le robot et le mur 162 | % du bas de l'arène. 163 | % 164 | % INFO.murs.dist_gauche : La distance entre le robot et le mur 165 | % de gauche de l'arène. 166 | % 167 | % INFO.murs.dist_droite : La distance entre le robot et le mur 168 | % de droite de l'arène. 169 | % 170 | % Si un mur est hors du rayon de perception du robot, la distance 171 | % indiquée sera NaN (inconnue). 172 | % 173 | % 174 | % INFO.nbVoisins : Le nombre d'autres robots présents dans le rayon 175 | % de perception du robot. 176 | % 177 | % INFO.voisins : La liste des autres robots présents dans le rayon 178 | % de perception du robot. Le robot peut lire les propriétés de ces 179 | % autres robots et interagir avec eux. 180 | % 181 | % Par exemple, 182 | % 183 | % INFO.voisins{i}.id vous donne l'identifiant du voisin i. 184 | % 185 | % INFO.voisins{i}.cible_detected permet de savoir si le voisin i 186 | % a détécté la cible. 187 | % 188 | % INFO.voisins{i}.move(vx,vy) permet d'ordonner au voisin i de se 189 | % déplacer le long de vx,vy. 190 | % 191 | % INFO.voisins{i}.set_info_cible(x,y) permet de donner les coordonnées 192 | % de la cible au voisin i. 193 | % 194 | % etc... 195 | % 196 | % Les informations contenues dans INFO sont essentielles pour adapter le 197 | % comportement de votre robot. 198 | 199 | function robot = update(robot, INFO) 200 | 201 | 202 | %%%%COLLISIONS MURS 203 | border_v = [0 0]; % direction opposée aux murs 204 | SAFE_BORDER = 0.2; % distance d'évitement des murs (20 cm) 205 | if INFO.murs.dist_droite < SAFE_BORDER 206 | border_v = border_v + [-0.1 0]; 207 | end 208 | if INFO.murs.dist_gauche < SAFE_BORDER 209 | border_v = border_v + [0.3 0.2]; 210 | end 211 | if INFO.murs.dist_haut < SAFE_BORDER 212 | border_v = border_v + [0 -0.1]; 213 | end 214 | if INFO.murs.dist_bas < SAFE_BORDER 215 | border_v = border_v + [0.2 0.3]; 216 | end 217 | 218 | %%%%RECHERCHES CIBLE 219 | start(robot); 220 | v = proximite(robot, INFO); 221 | v = v + border_v ; 222 | robot.move(v(1),v(2)); 223 | 224 | %%%%ATTAQUE CIBLE 225 | if (robot.cible_detected==1) 226 | for i=1:INFO.nbVoisins 227 | voisin = INFO.voisins{i}; 228 | voisin.set_info_cible(robot.cible_x, robot.cible_y); 229 | end 230 | if (robot.cible_attacked==0) 231 | vx = robot.cible_x-robot.x ; 232 | vy = robot.cible_y-robot.y ; 233 | robot.move(vx,vy); 234 | else 235 | robot.move(0,0); 236 | end 237 | end 238 | end 239 | 240 | 241 | end 242 | end 243 | 244 | -------------------------------------------------------------------------------- /utilities/ARobotarium.m: -------------------------------------------------------------------------------- 1 | classdef ARobotarium < handle 2 | % AROBOTARIUM This is an interface for the Robotarium class that 3 | % ensures the simulator and the robots match up properly. You should 4 | % definitely NOT MODIFY this file. Also, don't submit this file. 5 | 6 | properties (GetAccess = protected, SetAccess = protected) 7 | robot_handle % Handle for the robots patch objects 8 | robot_body % Base robot body position used for rendering 9 | 10 | boundary_patch % Path to denote the Robotarium's boundary 11 | end 12 | 13 | properties (Constant) 14 | time_step = 0.033 15 | max_linear_velocity = 0.2 16 | robot_diameter = 0.11 17 | wheel_radius = 0.016; 18 | base_length = 0.105; 19 | boundaries = [-1.6, 1.6, -1, 1]; 20 | end 21 | 22 | properties (GetAccess = public, SetAccess = protected) 23 | % Maximum wheel velocitry of the robots 24 | max_wheel_velocity = ARobotarium.max_linear_velocity/ARobotarium.wheel_radius; 25 | 26 | max_angular_velocity = ... 27 | 2*(ARobotarium.wheel_radius/ARobotarium.robot_diameter) ... 28 | *(ARobotarium.max_linear_velocity/ARobotarium.wheel_radius); 29 | 30 | number_of_robots 31 | figure_handle 32 | end 33 | 34 | properties (GetAccess = protected, SetAccess = protected) 35 | 36 | 37 | velocities 38 | poses 39 | left_leds 40 | right_leds 41 | % Figure handle for simulator 42 | 43 | show_figure 44 | end 45 | 46 | methods (Abstract) 47 | % Getters 48 | get_poses(this) 49 | 50 | % Initialization 51 | initialize(this, initial_conditions) 52 | 53 | %Update functions 54 | step(this); 55 | debug(this); 56 | end 57 | 58 | methods 59 | function this = ARobotarium(number_of_robots, show_figure, figure_handle) 60 | 61 | assert(number_of_robots >= 0 && number_of_robots <= 50, ... 62 | 'Number of robots (%i) must be >= 0 and <= 50', number_of_robots); 63 | this.number_of_robots = number_of_robots; 64 | N = number_of_robots; 65 | 66 | this.poses = zeros(3, N); 67 | this.show_figure = show_figure; 68 | this.velocities = zeros(2, N); 69 | this.left_leds = zeros(3, N); 70 | this.right_leds = zeros(3, N); 71 | 72 | if(show_figure) 73 | if(isempty(figure_handle)) 74 | this.figure_handle = figure(); 75 | else 76 | this.figure_handle = figure_handle; 77 | end 78 | 79 | this.initialize_visualization(); 80 | end 81 | end 82 | 83 | function agents = get_number_of_robots(this) 84 | agents = this.number_of_robots; 85 | end 86 | 87 | function this = set_velocities(this, ids, vs) 88 | N = size(vs, 2); 89 | 90 | assert(N<=this.number_of_robots, 'Row size of velocities (%i) must be <= to number of agents (%i)', ... 91 | N, this.number_of_robots); 92 | 93 | this.velocities(:, ids) = vs; 94 | end 95 | 96 | function this = set_left_leds(this, ids, rgbs) 97 | N = size(rgbs, 2); 98 | 99 | assert(N<=this.number_of_robots, 'Row size of rgb values (%i) must be <= to number of agents (%i)', ... 100 | N, this.number_of_robots); 101 | 102 | assert(all(all(rgbs(1:3, :) <= 255)) && all(all(rgbs(1:3, :) >= 0)), 'RGB commands must be between 0 and 255'); 103 | 104 | % Only set LED commands for the selected robots 105 | this.left_leds(:, ids) = rgbs; 106 | end 107 | 108 | function this = set_right_leds(this, ids, rgbs) 109 | N = size(rgbs, 2); 110 | 111 | assert(N<=this.number_of_robots, 'Row size of rgb values (%i) must be <= to number of agents (%i)', ... 112 | N, this.number_of_robots); 113 | 114 | assert(all(all(rgbs(1:3, :) <= 255)) && all(all(rgbs(1:3, :) >= 0)), 'RGB commands must be between 0 and 255'); 115 | 116 | % Only set LED commands for the selected robots 117 | this.right_leds(:, ids) = rgbs; 118 | end 119 | 120 | function iters = time2iters(this, time) 121 | iters = time / this.time_step; 122 | end 123 | end 124 | 125 | methods (Access = protected) 126 | 127 | function dxu = threshold(this, dxu) 128 | dxdd = this.uni_to_diff(dxu); 129 | 130 | to_thresh = abs(dxdd) > this.max_wheel_velocity; 131 | dxdd(to_thresh) = this.max_wheel_velocity*sign(dxdd(to_thresh)); 132 | 133 | dxu = this.diff_to_uni(dxdd); 134 | end 135 | 136 | function dxdd = uni_to_diff(this, dxu) 137 | r = this.wheel_radius; 138 | l = this.base_length; 139 | dxdd = [ 140 | (1/(2*r))*(2*dxu(1, :) - l*dxu(2, :)) ; ... 141 | (1/(2*r))*(2*dxu(1, :) + l*dxu(2, :)) 142 | ]; 143 | end 144 | 145 | function dxu = diff_to_uni(this, dxdd) 146 | r = this.wheel_radius; 147 | l = this.base_length; 148 | dxu = [ 149 | r/2*(dxdd(1, :) + dxdd(2, :)); 150 | r/l*(dxdd(2, :) - dxdd(1, :)) 151 | ]; 152 | end 153 | 154 | function errors = validate(this) 155 | % VALIDATE meant to be called on each iteration of STEP. 156 | % Checks that robots are operating normally. 157 | 158 | p = this.poses; 159 | b = this.boundaries; 160 | N = this.number_of_robots; 161 | errors = {}; 162 | 163 | for i = 1:N 164 | x = p(1, i); 165 | y = p(2, i); 166 | 167 | if(x < b(1) || x > b(2) || y < b(3) || y > b(4)) 168 | errors{end+1} = RobotariumError.RobotsOutsideBoundaries; 169 | end 170 | end 171 | 172 | for i = 1:(N-1) 173 | for j = i+1:N 174 | if(norm(p(1:2, i) - p(1:2, j)) <= ARobotarium.robot_diameter) 175 | errors{end+1} = RobotariumError.RobotsTooClose; 176 | end 177 | end 178 | end 179 | 180 | dxdd = this.uni_to_diff(this.velocities); 181 | exceeding = abs(dxdd) > this.max_wheel_velocity; 182 | if(any(any(exceeding))) 183 | errors{end+1} = RobotariumError.ExceededActuatorLimits; 184 | end 185 | end 186 | end 187 | 188 | % Visualization methods 189 | methods (Access = protected) 190 | 191 | % Initializes visualization of GRITSbots 192 | function initialize_visualization(this) 193 | % Initialize variables 194 | N = this.number_of_robots; 195 | offset = 0.05; 196 | 197 | % fig = figure; 198 | % this.figure_handle = fig; 199 | fig = this.figure_handle; 200 | 201 | % Plot Robotarium boundaries 202 | b = this.boundaries; 203 | boundary_points = {[b(1) b(2) b(2) b(1)], [b(3) b(3) b(4) b(4)]}; 204 | this.boundary_patch = patch('XData', boundary_points{1}, ... 205 | 'YData', boundary_points{2}, ... 206 | 'FaceColor', 'none', ... 207 | 'LineWidth', 3, ..., 208 | 'EdgeColor', [0, 0, 0]); 209 | 210 | set(fig, 'color', 'white'); 211 | 212 | % Set axis 213 | ax = fig.CurrentAxes; 214 | 215 | % Limit view to xMin/xMax/yMin/yMax 216 | axis(ax, [this.boundaries(1)-offset, this.boundaries(2)+offset, this.boundaries(3)-offset, this.boundaries(4)+offset]) 217 | set(ax, 'PlotBoxAspectRatio', [1 1 1], 'DataAspectRatio', [1 1 1]) 218 | 219 | % Store axes 220 | axis(ax, 'off') 221 | 222 | % Static legend 223 | setappdata(ax, 'LegendColorbarManualSpace', 1); 224 | setappdata(ax, 'LegendColorbarReclaimSpace', 1); 225 | 226 | % Apparently, this statement is necessary to avoid issues with 227 | % axes reappearing. 228 | hold on 229 | set(gcf , 'Position' , [139 288 1188 623]); 230 | 231 | this.robot_handle = cell(1, N); 232 | for i = 1:N 233 | data = gritsbot_patch; 234 | this.robot_body = data.vertices; 235 | x = this.poses(1, i); 236 | y = this.poses(2, i); 237 | th = this.poses(3, i) - pi/2; 238 | rotation_matrix = [ 239 | cos(th) -sin(th) x; 240 | sin(th) cos(th) y; 241 | 0 0 1]; 242 | transformed = this.robot_body*rotation_matrix'; 243 | this.robot_handle{i} = patch(... 244 | 'Vertices', transformed(:, 1:2), ... 245 | 'Faces', data.faces, ... 246 | 'FaceColor', 'flat', ... 247 | 'FaceVertexCData', data.colors, ... 248 | 'EdgeColor','none'); 249 | end 250 | end 251 | 252 | function draw_robots(this) 253 | for i = 1:this.number_of_robots 254 | x = this.poses(1, i); 255 | y = this.poses(2, i); 256 | th = this.poses(3, i) - pi/2; 257 | rotation_matrix = [... 258 | cos(th) -sin(th) x; 259 | sin(th) cos(th) y; 260 | 0 0 1 261 | ]; 262 | transformed = this.robot_body*rotation_matrix'; 263 | set(this.robot_handle{i}, 'Vertices', transformed(:, 1:2)); 264 | 265 | % Set LEDs 266 | left = this.left_leds/255; 267 | right = this.right_leds/255; 268 | 269 | this.robot_handle{i}.FaceVertexCData(4, :) = left(:, i); 270 | this.robot_handle{i}.FaceVertexCData(5, :) = right(:, i); 271 | end 272 | 273 | drawnow limitrate 274 | end 275 | end 276 | end 277 | --------------------------------------------------------------------------------