completeGL: 
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 ├── 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 |
Returns a completely connected graph Laplacian
L = completeGL(5); 70 |
function [ L ] = completeGL( n ) 71 | L = n * eye(n) - ones(n,n); 72 | end 73 |

Returns a cycle graph Laplacian
L = cycleGL(4) 70 |
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 |

Returns a line graph Laplacian of size n x n
L = lineGL(5) 70 |
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 |

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 |
Returns a timestamped filename with *.mat appended
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 |

Returns the agents within the 2-norm of the supplied agent
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 |

Returns a set of random poses distributed in the Robotarium workspace
initial_conditions = generate_initial_conditions(4); 70 |
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 |

Returns a random grab laplacian with a specified number of verticies and edges
L = randomGL(5, 3); 70 |
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 |

Returns a random, connected GL with v verticies and (v-1) + e edges
L = random_connectedGL(4, 3); 70 |
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 |
Returns a mapping
from single-integrator to unicycle dynamics
% 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 |
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 |
Creates a function to check for initialization. The function returns whether all the agents have been initialized and those that have already finished.
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 |
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 |
Returns a mapping from unicycle to single-integrator dynamics
and a mapping between their states
Using this particular method, the single-integrator dynamics must be computed in the single-integrator domain.
% 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 |
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 |
Returns a mapping from single-integrator to unicycle dynamics
and a mapping between their states
Using this particular method, the single-integrator dynamics must be computed in the single-integrator domain.
% 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 |
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 |