Compare commits
11 Commits
c47b7229ba
...
2604711c78
| Author | SHA1 | Date | |
|---|---|---|---|
| 2604711c78 | |||
| bcb3bc3da3 | |||
| 08e396c155 | |||
| df31c2f03c | |||
| ff02e8a1c6 | |||
| 2a48b1d469 | |||
| 7ba21fbaa7 | |||
| 40df9059e7 | |||
| 103e8b391b | |||
| 796e2f322a | |||
| ec202d7790 |
@@ -3,42 +3,40 @@ classdef agent
|
|||||||
% Identifiers
|
% Identifiers
|
||||||
label = "";
|
label = "";
|
||||||
|
|
||||||
% Sensor
|
|
||||||
sensorModel;
|
|
||||||
sensingLength = 0.05; % length parameter used by sensing function
|
|
||||||
|
|
||||||
% State
|
% State
|
||||||
lastPos = NaN(1, 3); % position from previous timestep
|
lastPos = NaN(1, 3); % position from previous timestep
|
||||||
pos = NaN(1, 3); % current position
|
pos = NaN(1, 3); % current position
|
||||||
vel = NaN(1, 3); % current velocity
|
|
||||||
pan = NaN; % pan angle
|
% Sensor
|
||||||
tilt = NaN; % tilt angle
|
sensorModel;
|
||||||
|
|
||||||
% Collision
|
% Collision
|
||||||
collisionGeometry;
|
collisionGeometry;
|
||||||
barrierFunction;
|
|
||||||
dBarrierFunction;
|
|
||||||
|
|
||||||
% FOV cone
|
% FOV cone
|
||||||
fovGeometry;
|
fovGeometry;
|
||||||
|
|
||||||
% Communication
|
% Communication
|
||||||
comRange = NaN;
|
|
||||||
commsGeometry = spherical;
|
commsGeometry = spherical;
|
||||||
lesserNeighbors = [];
|
lesserNeighbors = [];
|
||||||
|
|
||||||
|
% Performance
|
||||||
performance = 0;
|
performance = 0;
|
||||||
|
|
||||||
% Plotting
|
% Plotting
|
||||||
scatterPoints;
|
scatterPoints;
|
||||||
debug = false;
|
|
||||||
debugFig;
|
|
||||||
plotCommsGeometry = true;
|
plotCommsGeometry = true;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
properties (SetAccess = private, GetAccess = public)
|
||||||
|
initialStepSize = NaN;
|
||||||
|
stepDecayRate = NaN;
|
||||||
|
end
|
||||||
|
|
||||||
methods (Access = public)
|
methods (Access = public)
|
||||||
[obj] = initialize(obj, pos, vel, pan, tilt, collisionGeometry, sensorModel, guidanceModel, comRange, index, label);
|
[obj] = initialize(obj, pos, pan, tilt, collisionGeometry, sensorModel, guidanceModel, comRange, index, label);
|
||||||
[obj] = run(obj, domain, partitioning, t, index);
|
[obj] = run(obj, domain, partitioning, t, index, agents);
|
||||||
|
[partitioning] = partition(obj, agents, objective)
|
||||||
[obj, f] = plot(obj, ind, f);
|
[obj, f] = plot(obj, ind, f);
|
||||||
updatePlots(obj);
|
updatePlots(obj);
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
function obj = initialize(obj, pos, vel, pan, tilt, collisionGeometry, sensorModel, comRange, label, debug, plotCommsGeometry)
|
function obj = initialize(obj, pos, collisionGeometry, sensorModel, comRange, maxIter, initialStepSize, label, plotCommsGeometry)
|
||||||
arguments (Input)
|
arguments (Input)
|
||||||
obj (1, 1) {mustBeA(obj, 'agent')};
|
obj (1, 1) {mustBeA(obj, 'agent')};
|
||||||
pos (1, 3) double;
|
pos (1, 3) double;
|
||||||
vel (1, 3) double;
|
|
||||||
pan (1, 1) double;
|
|
||||||
tilt (1, 1) double;
|
|
||||||
collisionGeometry (1, 1) {mustBeGeometry};
|
collisionGeometry (1, 1) {mustBeGeometry};
|
||||||
sensorModel (1, 1) {mustBeSensor};
|
sensorModel (1, 1) {mustBeSensor};
|
||||||
comRange (1, 1) double;
|
comRange (1, 1) double;
|
||||||
|
maxIter (1, 1) double;
|
||||||
|
initialStepSize (1, 1) double = 0.2;
|
||||||
label (1, 1) string = "";
|
label (1, 1) string = "";
|
||||||
debug (1, 1) logical = false;
|
|
||||||
plotCommsGeometry (1, 1) logical = false;
|
plotCommsGeometry (1, 1) logical = false;
|
||||||
end
|
end
|
||||||
arguments (Output)
|
arguments (Output)
|
||||||
@@ -17,64 +15,20 @@ function obj = initialize(obj, pos, vel, pan, tilt, collisionGeometry, sensorMod
|
|||||||
end
|
end
|
||||||
|
|
||||||
obj.pos = pos;
|
obj.pos = pos;
|
||||||
obj.vel = vel;
|
|
||||||
obj.pan = pan;
|
|
||||||
obj.tilt = tilt;
|
|
||||||
obj.collisionGeometry = collisionGeometry;
|
obj.collisionGeometry = collisionGeometry;
|
||||||
obj.sensorModel = sensorModel;
|
obj.sensorModel = sensorModel;
|
||||||
obj.label = label;
|
obj.label = label;
|
||||||
obj.debug = debug;
|
|
||||||
obj.plotCommsGeometry = plotCommsGeometry;
|
obj.plotCommsGeometry = plotCommsGeometry;
|
||||||
|
obj.initialStepSize = initialStepSize;
|
||||||
|
obj.stepDecayRate = obj.initialStepSize / maxIter;
|
||||||
|
|
||||||
|
% Initialize performance vector
|
||||||
|
obj.performance = [0, NaN(1, maxIter), 0];
|
||||||
|
|
||||||
% Add spherical geometry based on com range
|
% Add spherical geometry based on com range
|
||||||
obj.commsGeometry = obj.commsGeometry.initialize(obj.pos, comRange, REGION_TYPE.COMMS, sprintf("%s Comms Geometry", obj.label));
|
obj.commsGeometry = obj.commsGeometry.initialize(obj.pos, comRange, REGION_TYPE.COMMS, sprintf("%s Comms Geometry", obj.label));
|
||||||
|
|
||||||
if obj.debug
|
|
||||||
obj.debugFig = figure;
|
|
||||||
tiledlayout(obj.debugFig, "TileSpacing", "tight", "Padding", "compact");
|
|
||||||
nexttile;
|
|
||||||
axes(obj.debugFig.Children(1).Children(1));
|
|
||||||
axis(obj.debugFig.Children(1).Children(1), "image");
|
|
||||||
xlabel(obj.debugFig.Children(1).Children(1), "X"); ylabel(obj.debugFig.Children(1).Children(1), "Y");
|
|
||||||
title(obj.debugFig.Children(1).Children(1), "Objective");
|
|
||||||
nexttile;
|
|
||||||
axes(obj.debugFig.Children(1).Children(1));
|
|
||||||
axis(obj.debugFig.Children(1).Children(1), "image");
|
|
||||||
xlabel(obj.debugFig.Children(1).Children(1), "X"); ylabel(obj.debugFig.Children(1).Children(1), "Y");
|
|
||||||
title(obj.debugFig.Children(1).Children(1), "Sensor Performance");
|
|
||||||
nexttile;
|
|
||||||
axes(obj.debugFig.Children(1).Children(1));
|
|
||||||
axis(obj.debugFig.Children(1).Children(1), "image");
|
|
||||||
xlabel(obj.debugFig.Children(1).Children(1), "X"); ylabel(obj.debugFig.Children(1).Children(1), "Y");
|
|
||||||
title(obj.debugFig.Children(1).Children(1), "Gradient Objective");
|
|
||||||
nexttile;
|
|
||||||
axes(obj.debugFig.Children(1).Children(1));
|
|
||||||
axis(obj.debugFig.Children(1).Children(1), "image");
|
|
||||||
xlabel(obj.debugFig.Children(1).Children(1), "X"); ylabel(obj.debugFig.Children(1).Children(1), "Y");
|
|
||||||
title(obj.debugFig.Children(1).Children(1), "Gradient Sensor Performance");
|
|
||||||
nexttile;
|
|
||||||
axes(obj.debugFig.Children(1).Children(1));
|
|
||||||
axis(obj.debugFig.Children(1).Children(1), "image");
|
|
||||||
xlabel(obj.debugFig.Children(1).Children(1), "X"); ylabel(obj.debugFig.Children(1).Children(1), "Y");
|
|
||||||
title(obj.debugFig.Children(1).Children(1), "Sensor Performance x Gradient Objective");
|
|
||||||
nexttile;
|
|
||||||
axes(obj.debugFig.Children(1).Children(1));
|
|
||||||
axis(obj.debugFig.Children(1).Children(1), "image");
|
|
||||||
xlabel(obj.debugFig.Children(1).Children(1), "X"); ylabel(obj.debugFig.Children(1).Children(1), "Y");
|
|
||||||
title(obj.debugFig.Children(1).Children(1), "Gradient Sensor Performance x Objective");
|
|
||||||
nexttile;
|
|
||||||
axes(obj.debugFig.Children(1).Children(1));
|
|
||||||
axis(obj.debugFig.Children(1).Children(1), "image");
|
|
||||||
xlabel(obj.debugFig.Children(1).Children(1), "X"); ylabel(obj.debugFig.Children(1).Children(1), "Y");
|
|
||||||
title(obj.debugFig.Children(1).Children(1), "Agent Performance (C)");
|
|
||||||
nexttile;
|
|
||||||
axes(obj.debugFig.Children(1).Children(1));
|
|
||||||
axis(obj.debugFig.Children(1).Children(1), "image");
|
|
||||||
xlabel(obj.debugFig.Children(1).Children(1), "X"); ylabel(obj.debugFig.Children(1).Children(1), "Y");
|
|
||||||
title(obj.debugFig.Children(1).Children(1), "Gradient Agent Performance (del C)");
|
|
||||||
end
|
|
||||||
|
|
||||||
% Initialize FOV cone
|
% Initialize FOV cone
|
||||||
obj.fovGeometry = cone;
|
obj.fovGeometry = cone;
|
||||||
obj.fovGeometry = obj.fovGeometry.initialize([obj.pos(1:2), 0], tand(obj.sensorModel.alphaTilt) * obj.pos(3), obj.pos(3), REGION_TYPE.FOV, sprintf("%s FOV", obj.label));
|
obj.fovGeometry = obj.fovGeometry.initialize([obj.pos(1:3)], tand(obj.sensorModel.alphaTilt) * obj.pos(3), obj.pos(3), REGION_TYPE.FOV, sprintf("%s FOV", obj.label));
|
||||||
end
|
end
|
||||||
35
@agent/partition.m
Normal file
35
@agent/partition.m
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
function [partitioning] = partition(obj, agents, objective)
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'agent')};
|
||||||
|
agents (:, 1) {mustBeA(agents, 'cell')};
|
||||||
|
objective (1, 1) {mustBeA(objective, 'sensingObjective')};
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
partitioning (:, :) double;
|
||||||
|
end
|
||||||
|
|
||||||
|
% Assess sensing performance of each agent at each sample point
|
||||||
|
% in the domain
|
||||||
|
agentPerformances = cellfun(@(x) reshape(x.sensorModel.sensorPerformance(x.pos, [objective.X(:), objective.Y(:), zeros(size(objective.X(:)))]), size(objective.X)), agents, 'UniformOutput', false);
|
||||||
|
agentPerformances{end + 1} = objective.sensorPerformanceMinimum * ones(size(agentPerformances{end})); % add additional layer to represent the threshold that has to be cleared for assignment to any partiton
|
||||||
|
agentPerformances = cat(3, agentPerformances{:});
|
||||||
|
|
||||||
|
% Get highest performance value at each point
|
||||||
|
[~, idx] = max(agentPerformances, [], 3);
|
||||||
|
|
||||||
|
% Collect agent indices in the same way as performance
|
||||||
|
indices = 1:size(agents, 1);
|
||||||
|
agentInds = squeeze(tensorprod(indices, ones(size(objective.X))));
|
||||||
|
if size(agentInds, 1) ~= size(agents, 1)
|
||||||
|
agentInds = reshape(agentInds, [size(agents, 1), size(agentInds)]); % needed for cases with 1 agent where prior squeeze is too agressive
|
||||||
|
end
|
||||||
|
agentInds = num2cell(agentInds, 2:3);
|
||||||
|
agentInds = cellfun(@(x) squeeze(x), agentInds, 'UniformOutput', false);
|
||||||
|
agentInds{end + 1} = zeros(size(agentInds{end})); % index for no assignment
|
||||||
|
agentInds = cat(3, agentInds{:});
|
||||||
|
|
||||||
|
% Use highest performing agent's index to form partitions
|
||||||
|
[m, n, ~] = size(agentInds);
|
||||||
|
[jj, kk] = ndgrid(1:m, 1:n);
|
||||||
|
partitioning = agentInds(sub2ind(size(agentInds), jj, kk, idx));
|
||||||
|
end
|
||||||
@@ -36,5 +36,6 @@ function [obj, f] = plot(obj, ind, f)
|
|||||||
end
|
end
|
||||||
|
|
||||||
% Plot FOV geometry
|
% Plot FOV geometry
|
||||||
[obj.fovGeometry, f] = obj.fovGeometry.plot(ind, f);
|
maxAlt = f.Children(1).Children(end).ZLim(2); % to avoid scaling the FOV geometry as the sim runs, let's just make it really big and hide the excess under the floor of the domain. Check the domain altitude to figure out how big it needs to be to achieve this deception.
|
||||||
|
[obj.fovGeometry, f] = obj.fovGeometry.plot(ind, f, maxAlt);
|
||||||
end
|
end
|
||||||
163
@agent/run.m
163
@agent/run.m
@@ -1,10 +1,11 @@
|
|||||||
function obj = run(obj, domain, partitioning, t, index)
|
function obj = run(obj, domain, partitioning, timestepIndex, index, agents)
|
||||||
arguments (Input)
|
arguments (Input)
|
||||||
obj (1, 1) {mustBeA(obj, 'agent')};
|
obj (1, 1) {mustBeA(obj, 'agent')};
|
||||||
domain (1, 1) {mustBeGeometry};
|
domain (1, 1) {mustBeGeometry};
|
||||||
partitioning (:, :) double;
|
partitioning (:, :) double;
|
||||||
t (1, 1) double;
|
timestepIndex (1, 1) double;
|
||||||
index (1, 1) double;
|
index (1, 1) double;
|
||||||
|
agents (:, 1) {mustBeA(agents, 'cell')};
|
||||||
end
|
end
|
||||||
arguments (Output)
|
arguments (Output)
|
||||||
obj (1, 1) {mustBeA(obj, 'agent')};
|
obj (1, 1) {mustBeA(obj, 'agent')};
|
||||||
@@ -12,136 +13,72 @@ function obj = run(obj, domain, partitioning, t, index)
|
|||||||
|
|
||||||
% Collect objective function values across partition
|
% Collect objective function values across partition
|
||||||
partitionMask = partitioning == index;
|
partitionMask = partitioning == index;
|
||||||
|
if ~unique(partitionMask)
|
||||||
|
% This agent has no partition, maintain current state
|
||||||
|
return;
|
||||||
|
end
|
||||||
objectiveValues = domain.objective.values(partitionMask); % f(omega) on W_n
|
objectiveValues = domain.objective.values(partitionMask); % f(omega) on W_n
|
||||||
|
|
||||||
% Compute sensor performance across partition
|
% Compute sensor performance on partition
|
||||||
maskedX = domain.objective.X(partitionMask);
|
maskedX = domain.objective.X(partitionMask);
|
||||||
maskedY = domain.objective.Y(partitionMask);
|
maskedY = domain.objective.Y(partitionMask);
|
||||||
zFactor = 1;
|
|
||||||
sensorValues = obj.sensorModel.sensorPerformance(obj.pos, obj.pan, obj.tilt, [maskedX, maskedY, zeros(size(maskedX))]); % S_n(omega, P_n) on W_n
|
|
||||||
sensorValuesLower = obj.sensorModel.sensorPerformance(obj.pos - [0, 0, zFactor * domain.objective.discretizationStep], obj.pan, obj.tilt, [maskedX, maskedY, zeros(size(maskedX))]); % S_n(omega, P_n - [0, 0, z]) on W_n
|
|
||||||
sensorValuesHigher = obj.sensorModel.sensorPerformance(obj.pos + [0, 0, zFactor * domain.objective.discretizationStep], obj.pan, obj.tilt, [maskedX, maskedY, zeros(size(maskedX))]); % S_n(omega, P_n - [0, 0, z]) on W_n
|
|
||||||
|
|
||||||
% Put the values back into the form of the partition to enable basic operations on this data
|
% Compute agent performance at the current position and each delta position +/- X, Y, Z
|
||||||
|
delta = domain.objective.discretizationStep; % smallest possible step size that gets different results
|
||||||
|
deltaApplicator = [0, 0, 0; 1, 0, 0; -1, 0, 0; 0, 1, 0; 0, -1, 0; 0, 0, 1; 0, 0, -1]; % none, +X, -X, +Y, -Y, +Z, -Z
|
||||||
|
C_delta = NaN(7, 1); % agent performance at delta steps in each direction
|
||||||
|
for ii = 1:7
|
||||||
|
% Apply delta to position
|
||||||
|
pos = obj.pos + delta * deltaApplicator(ii, 1:3);
|
||||||
|
|
||||||
|
% Compute performance values on partition
|
||||||
|
if ii < 5
|
||||||
|
% Compute sensing performance
|
||||||
|
sensorValues = obj.sensorModel.sensorPerformance(pos, [maskedX, maskedY, zeros(size(maskedX))]); % S_n(omega, P_n) on W_n
|
||||||
|
% Objective performance does not change for 0, +/- X, Y steps.
|
||||||
|
% Those values are computed once before the loop and are only
|
||||||
|
% recomputed when +/- Z steps are applied
|
||||||
|
else
|
||||||
|
% Redo partitioning for Z stepping only
|
||||||
|
partitioning = obj.partition(agents, domain.objective);
|
||||||
|
|
||||||
|
% Recompute partiton-derived performance values for objective
|
||||||
|
partitionMask = partitioning == index;
|
||||||
|
objectiveValues = domain.objective.values(partitionMask); % f(omega) on W_n
|
||||||
|
|
||||||
|
% Recompute partiton-derived performance values for sensing
|
||||||
|
maskedX = domain.objective.X(partitionMask);
|
||||||
|
maskedY = domain.objective.Y(partitionMask);
|
||||||
|
sensorValues = obj.sensorModel.sensorPerformance(pos, [maskedX, maskedY, zeros(size(maskedX))]); % S_n(omega, P_n) on W_n
|
||||||
|
end
|
||||||
|
|
||||||
|
% Rearrange data into image arrays
|
||||||
F = NaN(size(partitionMask));
|
F = NaN(size(partitionMask));
|
||||||
F(partitionMask) = objectiveValues;
|
F(partitionMask) = objectiveValues;
|
||||||
S = NaN(size(partitionMask));
|
S = NaN(size(partitionMask));
|
||||||
Slower = S;
|
|
||||||
Shigher = S;
|
|
||||||
S(partitionMask) = sensorValues;
|
S(partitionMask) = sensorValues;
|
||||||
Slower(partitionMask) = sensorValuesLower;
|
|
||||||
Shigher(partitionMask) = sensorValuesHigher;
|
|
||||||
|
|
||||||
% Find agent's performance
|
% Compute agent performance
|
||||||
C = S .* F;
|
C = S .* F;
|
||||||
obj.performance = [obj.performance, sum(C(~isnan(C)))]; % at current Z only
|
C_delta(ii) = sum(C(~isnan(C)));
|
||||||
C = cat(3, Shigher, S, Slower) .* F;
|
|
||||||
|
|
||||||
% Compute gradient on agent's performance
|
|
||||||
[gradCX, gradCY, gradCZ] = gradient(C, domain.objective.discretizationStep); % grad C
|
|
||||||
gradC = cat(4, gradCX, gradCY, gradCZ);
|
|
||||||
nGradC = vecnorm(gradC, 2, 4);
|
|
||||||
|
|
||||||
if obj.debug
|
|
||||||
% Compute additional component-level values for diagnosing issues
|
|
||||||
[gradSensorPerformanceX, gradSensorPerformanceY] = gradient(S, domain.objective.discretizationStep); % grad S_n
|
|
||||||
[gradObjectiveX, gradObjectiveY] = gradient(F, domain.objective.discretizationStep); % grad f
|
|
||||||
gradS = cat(3, gradSensorPerformanceX, gradSensorPerformanceY, zeros(size(gradSensorPerformanceX))); % grad S_n
|
|
||||||
gradF = cat(3, gradObjectiveX, gradObjectiveY, zeros(size(gradObjectiveX))); % grad f
|
|
||||||
|
|
||||||
ii = 8;
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "on");
|
|
||||||
cla(obj.debugFig.Children(1).Children(ii));
|
|
||||||
imagesc(obj.debugFig.Children(1).Children(ii), F./max(F, [], 'all'));
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "off");
|
|
||||||
ii = ii - 1;
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "on");
|
|
||||||
cla(obj.debugFig.Children(1).Children(ii));
|
|
||||||
imagesc(obj.debugFig.Children(1).Children(ii), S./max(S, [], 'all'));
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "off");
|
|
||||||
ii = ii - 1;
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "on");
|
|
||||||
cla(obj.debugFig.Children(1).Children(ii));
|
|
||||||
imagesc(obj.debugFig.Children(1).Children(ii), vecnorm(gradF, 2, 3)./max(vecnorm(gradF, 2, 3), [], 'all'));
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "off");
|
|
||||||
ii = ii - 1;
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "on");
|
|
||||||
cla(obj.debugFig.Children(1).Children(ii));
|
|
||||||
imagesc(obj.debugFig.Children(1).Children(ii), vecnorm(gradS, 2, 3)./max(vecnorm(gradS, 2, 3), [], 'all'));
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "off");
|
|
||||||
ii = ii - 1;
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "on");
|
|
||||||
cla(obj.debugFig.Children(1).Children(ii));
|
|
||||||
imagesc(obj.debugFig.Children(1).Children(ii), S .* vecnorm(gradF, 2, 3)./max(vecnorm(gradF, 2, 3), [], 'all'));
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "off");
|
|
||||||
ii = ii - 1;
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "on");
|
|
||||||
cla(obj.debugFig.Children(1).Children(ii));
|
|
||||||
imagesc(obj.debugFig.Children(1).Children(ii), F .* vecnorm(gradS, 2, 3)./max(vecnorm(gradS, 2, 3), [], 'all')./(max(F .* vecnorm(gradS, 2, 3)./max(vecnorm(gradS, 2, 3), [], 'all'))));
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "off");
|
|
||||||
|
|
||||||
ii = ii - 1;
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "on");
|
|
||||||
cla(obj.debugFig.Children(1).Children(ii));
|
|
||||||
imagesc(obj.debugFig.Children(1).Children(ii), C./max(C, [], 'all'));
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "off");
|
|
||||||
ii = ii - 1;
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "on");
|
|
||||||
cla(obj.debugFig.Children(1).Children(ii));
|
|
||||||
imagesc(obj.debugFig.Children(1).Children(ii), nGradC./max(nGradC, [], 'all'));
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "off");
|
|
||||||
[x, y] = find(nGradC == max(nGradC, [], "all"));
|
|
||||||
|
|
||||||
% just pick one
|
|
||||||
r = randi([1, size(x, 1)]);
|
|
||||||
x = x(r); y = y(r);
|
|
||||||
|
|
||||||
% switch them
|
|
||||||
temp = x;
|
|
||||||
x = y;
|
|
||||||
y = temp;
|
|
||||||
|
|
||||||
% find objective location in discrete domain
|
|
||||||
[~, xIdx] = find(domain.objective.groundPos(1) == domain.objective.X);
|
|
||||||
xIdx = unique(xIdx);
|
|
||||||
[yIdx, ~] = find(domain.objective.groundPos(2) == domain.objective.Y);
|
|
||||||
yIdx = unique(yIdx);
|
|
||||||
for ii = 8:-1:1
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "on");
|
|
||||||
% plot GA selection
|
|
||||||
scatter(obj.debugFig.Children(1).Children(ii), x, y, 'go');
|
|
||||||
scatter(obj.debugFig.Children(1).Children(ii), x, y, 'g+');
|
|
||||||
% plot objective center
|
|
||||||
scatter(obj.debugFig.Children(1).Children(ii), xIdx, yIdx, 'ro');
|
|
||||||
scatter(obj.debugFig.Children(1).Children(ii), xIdx, yIdx, 'r+');
|
|
||||||
hold(obj.debugFig.Children(1).Children(ii), "off");
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
% return now if there is no data to work with, and do not move
|
% Store agent performance at current time and place
|
||||||
if all(isnan(nGradC), 'all')
|
obj.performance(timestepIndex + 1) = C_delta(1);
|
||||||
return;
|
|
||||||
end
|
|
||||||
|
|
||||||
% Use largest grad(C) value to find the direction of the next position
|
% Compute gradient by finite central differences
|
||||||
[xNextIdx, yNextIdx, zNextIdx] = ind2sub(size(nGradC), find(nGradC == max(nGradC, [], 'all')));
|
gradC = [(C_delta(2)-C_delta(3))/(2*delta), (C_delta(4)-C_delta(5))/(2*delta), (C_delta(6)-C_delta(7))/(2*delta)];
|
||||||
% switch them
|
|
||||||
temp = xNextIdx;
|
|
||||||
xNextIdx = yNextIdx;
|
|
||||||
yNextIdx = temp;
|
|
||||||
|
|
||||||
roundingScale = 10^-log10(domain.objective.discretizationStep);
|
% Compute scaling factor
|
||||||
zKey = zFactor * [1; 0; -1];
|
targetRate = obj.initialStepSize - obj.stepDecayRate * timestepIndex; % slow down as you get closer
|
||||||
pNext = [floor(roundingScale .* mean(unique(domain.objective.X(:, xNextIdx))))./roundingScale, floor(roundingScale .* mean(unique(domain.objective.Y(yNextIdx, :))))./roundingScale, obj.pos(3) + zKey(zNextIdx)]; % have to do some unfortunate rounding here sometimes
|
rateFactor = targetRate / norm(gradC);
|
||||||
|
|
||||||
% Determine next position
|
% Compute unconstrained next position
|
||||||
vDir = (pNext - obj.pos)./norm(pNext - obj.pos, 2);
|
pNext = obj.pos + rateFactor * gradC;
|
||||||
rate = 0.1 - 0.0004 * t; % slow down as you get closer, coming to a stop by the end
|
|
||||||
nextPos = obj.pos + vDir * rate;
|
|
||||||
|
|
||||||
% Move to next position
|
% Move to next position
|
||||||
obj.lastPos = obj.pos;
|
obj.lastPos = obj.pos;
|
||||||
obj.pos = nextPos;
|
obj.pos = pNext;
|
||||||
|
|
||||||
% Reinitialize collision geometry in the new position
|
% Reinitialize collision geometry in the new position
|
||||||
d = obj.pos - obj.collisionGeometry.center;
|
d = obj.pos - obj.collisionGeometry.center;
|
||||||
|
|||||||
@@ -5,6 +5,13 @@ function updatePlots(obj)
|
|||||||
arguments (Output)
|
arguments (Output)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
% Find change in agent position since last timestep
|
||||||
|
deltaPos = obj.pos - obj.lastPos;
|
||||||
|
if all(isnan(deltaPos)) || all(deltaPos == zeros(1, 3))
|
||||||
|
% Agent did not move, so nothing has to move on the plots
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
% Scatterplot point positions
|
% Scatterplot point positions
|
||||||
for ii = 1:size(obj.scatterPoints, 1)
|
for ii = 1:size(obj.scatterPoints, 1)
|
||||||
obj.scatterPoints(ii).XData = obj.pos(1);
|
obj.scatterPoints(ii).XData = obj.pos(1);
|
||||||
@@ -12,9 +19,6 @@ function updatePlots(obj)
|
|||||||
obj.scatterPoints(ii).ZData = obj.pos(3);
|
obj.scatterPoints(ii).ZData = obj.pos(3);
|
||||||
end
|
end
|
||||||
|
|
||||||
% Find change in agent position since last timestep
|
|
||||||
deltaPos = obj.pos - obj.lastPos;
|
|
||||||
|
|
||||||
% Collision geometry edges
|
% Collision geometry edges
|
||||||
for jj = 1:size(obj.collisionGeometry.lines, 2)
|
for jj = 1:size(obj.collisionGeometry.lines, 2)
|
||||||
% Update plotting
|
% Update plotting
|
||||||
@@ -39,6 +43,7 @@ function updatePlots(obj)
|
|||||||
% Update FOV geometry surfaces
|
% Update FOV geometry surfaces
|
||||||
for jj = 1:size(obj.fovGeometry.surface, 2)
|
for jj = 1:size(obj.fovGeometry.surface, 2)
|
||||||
% Update each plot
|
% Update each plot
|
||||||
|
% obj.fovGeometry = obj.fovGeometry.plot(obj.spatialPlotIndices)
|
||||||
obj.fovGeometry.surface(jj).XData = obj.fovGeometry.surface(jj).XData + deltaPos(1);
|
obj.fovGeometry.surface(jj).XData = obj.fovGeometry.surface(jj).XData + deltaPos(1);
|
||||||
obj.fovGeometry.surface(jj).YData = obj.fovGeometry.surface(jj).YData + deltaPos(2);
|
obj.fovGeometry.surface(jj).YData = obj.fovGeometry.surface(jj).YData + deltaPos(2);
|
||||||
obj.fovGeometry.surface(jj).ZData = obj.fovGeometry.surface(jj).ZData + deltaPos(3);
|
obj.fovGeometry.surface(jj).ZData = obj.fovGeometry.surface(jj).ZData + deltaPos(3);
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ function [obj] = constrainMotion(obj)
|
|||||||
|
|
||||||
agents = [obj.agents{:}];
|
agents = [obj.agents{:}];
|
||||||
v = reshape(([agents.pos] - [agents.lastPos])./obj.timestep, 3, size(obj.agents, 1))';
|
v = reshape(([agents.pos] - [agents.lastPos])./obj.timestep, 3, size(obj.agents, 1))';
|
||||||
|
if all(isnan(v), 'all') || all(v == zeros(size(obj.agents, 1), 3), 'all')
|
||||||
|
% Agents are not attempting to move, so there is no motion to be
|
||||||
|
% constrained
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
% Initialize QP based on number of agents and obstacles
|
% Initialize QP based on number of agents and obstacles
|
||||||
nAOPairs = size(obj.agents, 1) * size(obj.obstacles, 1); % unique agent/obstacle pairs
|
nAOPairs = size(obj.agents, 1) * size(obj.obstacles, 1); % unique agent/obstacle pairs
|
||||||
@@ -34,7 +39,7 @@ function [obj] = constrainMotion(obj)
|
|||||||
|
|
||||||
A(kk, (3 * ii - 2):(3 * ii)) = -2 * (agents(ii).pos - agents(jj).pos);
|
A(kk, (3 * ii - 2):(3 * ii)) = -2 * (agents(ii).pos - agents(jj).pos);
|
||||||
A(kk, (3 * jj - 2):(3 * jj)) = -A(kk, (3 * ii - 2):(3 * ii));
|
A(kk, (3 * jj - 2):(3 * jj)) = -A(kk, (3 * ii - 2):(3 * ii));
|
||||||
b(kk) = obj.barrierGain * h(ii, jj)^3;
|
b(kk) = obj.barrierGain * h(ii, jj)^obj.barrierExponent;
|
||||||
kk = kk + 1;
|
kk = kk + 1;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -49,7 +54,7 @@ function [obj] = constrainMotion(obj)
|
|||||||
hObs(ii, jj) = dot(agents(ii).pos - cPos, agents(ii).pos - cPos) - agents(ii).collisionGeometry.radius^2;
|
hObs(ii, jj) = dot(agents(ii).pos - cPos, agents(ii).pos - cPos) - agents(ii).collisionGeometry.radius^2;
|
||||||
|
|
||||||
A(kk, (3 * ii - 2):(3 * ii)) = -2 * (agents(ii).pos - cPos);
|
A(kk, (3 * ii - 2):(3 * ii)) = -2 * (agents(ii).pos - cPos);
|
||||||
b(kk) = obj.barrierGain * hObs(ii, jj)^3;
|
b(kk) = obj.barrierGain * hObs(ii, jj)^obj.barrierExponent;
|
||||||
|
|
||||||
kk = kk + 1;
|
kk = kk + 1;
|
||||||
end
|
end
|
||||||
@@ -62,37 +67,37 @@ function [obj] = constrainMotion(obj)
|
|||||||
% X minimum
|
% X minimum
|
||||||
h_xMin = (agents(ii).pos(1) - obj.domain.minCorner(1)) - agents(ii).collisionGeometry.radius;
|
h_xMin = (agents(ii).pos(1) - obj.domain.minCorner(1)) - agents(ii).collisionGeometry.radius;
|
||||||
A(kk, (3 * ii - 2):(3 * ii)) = [-1, 0, 0];
|
A(kk, (3 * ii - 2):(3 * ii)) = [-1, 0, 0];
|
||||||
b(kk) = obj.barrierGain * h_xMin^3;
|
b(kk) = obj.barrierGain * h_xMin^obj.barrierExponent;
|
||||||
kk = kk + 1;
|
kk = kk + 1;
|
||||||
|
|
||||||
% X maximum
|
% X maximum
|
||||||
h_xMax = (obj.domain.maxCorner(1) - agents(ii).pos(1)) - agents(ii).collisionGeometry.radius;
|
h_xMax = (obj.domain.maxCorner(1) - agents(ii).pos(1)) - agents(ii).collisionGeometry.radius;
|
||||||
A(kk, (3 * ii - 2):(3 * ii)) = [1, 0, 0];
|
A(kk, (3 * ii - 2):(3 * ii)) = [1, 0, 0];
|
||||||
b(kk) = obj.barrierGain * h_xMax^3;
|
b(kk) = obj.barrierGain * h_xMax^obj.barrierExponent;
|
||||||
kk = kk + 1;
|
kk = kk + 1;
|
||||||
|
|
||||||
% Y minimum
|
% Y minimum
|
||||||
h_yMin = (agents(ii).pos(2) - obj.domain.minCorner(2)) - agents(ii).collisionGeometry.radius;
|
h_yMin = (agents(ii).pos(2) - obj.domain.minCorner(2)) - agents(ii).collisionGeometry.radius;
|
||||||
A(kk, (3 * ii - 2):(3 * ii)) = [0, -1, 0];
|
A(kk, (3 * ii - 2):(3 * ii)) = [0, -1, 0];
|
||||||
b(kk) = obj.barrierGain * h_yMin^3;
|
b(kk) = obj.barrierGain * h_yMin^obj.barrierExponent;
|
||||||
kk = kk + 1;
|
kk = kk + 1;
|
||||||
|
|
||||||
% Y maximum
|
% Y maximum
|
||||||
h_yMax = (obj.domain.maxCorner(2) - agents(ii).pos(2)) - agents(ii).collisionGeometry.radius;
|
h_yMax = (obj.domain.maxCorner(2) - agents(ii).pos(2)) - agents(ii).collisionGeometry.radius;
|
||||||
A(kk, (3 * ii - 2):(3 * ii)) = [0, 1, 0];
|
A(kk, (3 * ii - 2):(3 * ii)) = [0, 1, 0];
|
||||||
b(kk) = obj.barrierGain * h_yMax^3;
|
b(kk) = obj.barrierGain * h_yMax^obj.barrierExponent;
|
||||||
kk = kk + 1;
|
kk = kk + 1;
|
||||||
|
|
||||||
% Z minimum
|
% Z minimum
|
||||||
h_zMin = (agents(ii).pos(3) - obj.domain.minCorner(3)) - agents(ii).collisionGeometry.radius;
|
h_zMin = (agents(ii).pos(3) - obj.domain.minCorner(3)) - agents(ii).collisionGeometry.radius;
|
||||||
A(kk, (3 * ii - 2):(3 * ii)) = [0, 0, -1];
|
A(kk, (3 * ii - 2):(3 * ii)) = [0, 0, -1];
|
||||||
b(kk) = obj.barrierGain * h_zMin^3;
|
b(kk) = obj.barrierGain * h_zMin^obj.barrierExponent;
|
||||||
kk = kk + 1;
|
kk = kk + 1;
|
||||||
|
|
||||||
% Z maximum
|
% Z maximum
|
||||||
h_zMax = (obj.domain.maxCorner(2) - agents(ii).pos(2)) - agents(ii).collisionGeometry.radius;
|
h_zMax = (obj.domain.maxCorner(2) - agents(ii).pos(2)) - agents(ii).collisionGeometry.radius;
|
||||||
A(kk, (3 * ii - 2):(3 * ii)) = [0, 0, 1];
|
A(kk, (3 * ii - 2):(3 * ii)) = [0, 0, 1];
|
||||||
b(kk) = obj.barrierGain * h_zMax^3;
|
b(kk) = obj.barrierGain * h_zMax^obj.barrierExponent;
|
||||||
kk = kk + 1;
|
kk = kk + 1;
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -109,11 +114,7 @@ function [obj] = constrainMotion(obj)
|
|||||||
|
|
||||||
A(kk, (3 * ii - 2):(3 * ii)) = 2 * (agents(ii).pos - agents(jj).pos);
|
A(kk, (3 * ii - 2):(3 * ii)) = 2 * (agents(ii).pos - agents(jj).pos);
|
||||||
A(kk, (3 * jj - 2):(3 * jj)) = -A(kk, (3 * ii - 2):(3 * ii));
|
A(kk, (3 * jj - 2):(3 * jj)) = -A(kk, (3 * ii - 2):(3 * ii));
|
||||||
b(kk) = obj.barrierGain * hComms(ii, jj);
|
b(kk) = obj.barrierGain * hComms(ii, jj)^obj.barrierExponent;
|
||||||
|
|
||||||
% dVNominal = v(ii, 1:3) - v(jj, 1:3); % nominal velocities
|
|
||||||
% h_dot_nom = -2 * (agents(ii).pos - agents(jj).pos) * dVNominal';
|
|
||||||
% b(kk) = -h_dot_nom + obj.barrierGain * hComms(ii, jj)^3;
|
|
||||||
|
|
||||||
kk = kk + 1;
|
kk = kk + 1;
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
function obj = initialize(obj, domain, objective, agents, minAlt, timestep, partitoningFreq, maxIter, obstacles, makePlots, makeVideo)
|
function [obj] = initialize(obj, domain, agents, barrierGain, barrierExponent, minAlt, timestep, maxIter, obstacles, makePlots, makeVideo)
|
||||||
arguments (Input)
|
arguments (Input)
|
||||||
obj (1, 1) {mustBeA(obj, 'miSim')};
|
obj (1, 1) {mustBeA(obj, 'miSim')};
|
||||||
domain (1, 1) {mustBeGeometry};
|
domain (1, 1) {mustBeGeometry};
|
||||||
objective (1, 1) {mustBeA(objective, 'sensingObjective')};
|
|
||||||
agents (:, 1) cell;
|
agents (:, 1) cell;
|
||||||
|
barrierGain (1, 1) double = 100;
|
||||||
|
barrierExponent (1, 1) double = 3;
|
||||||
minAlt (1, 1) double = 1;
|
minAlt (1, 1) double = 1;
|
||||||
timestep (:, 1) double = 0.05;
|
timestep (:, 1) double = 0.05;
|
||||||
partitoningFreq (:, 1) double = 0.25
|
|
||||||
maxIter (:, 1) double = 1000;
|
maxIter (:, 1) double = 1000;
|
||||||
obstacles (:, 1) cell {mustBeGeometry} = cell(0, 1);
|
obstacles (:, 1) cell {mustBeGeometry} = cell(0, 1);
|
||||||
makePlots(1, 1) logical = true;
|
makePlots(1, 1) logical = true;
|
||||||
@@ -26,6 +26,9 @@ function obj = initialize(obj, domain, objective, agents, minAlt, timestep, part
|
|||||||
end
|
end
|
||||||
obj.makeVideo = makeVideo;
|
obj.makeVideo = makeVideo;
|
||||||
|
|
||||||
|
% Generate artifact(s) name
|
||||||
|
obj.artifactName = strcat(string(datetime('now'), 'yyyy_MM_dd_HH_mm_ss'));
|
||||||
|
|
||||||
% Define simulation time parameters
|
% Define simulation time parameters
|
||||||
obj.timestep = timestep;
|
obj.timestep = timestep;
|
||||||
obj.timestepIndex = 0;
|
obj.timestepIndex = 0;
|
||||||
@@ -33,22 +36,17 @@ function obj = initialize(obj, domain, objective, agents, minAlt, timestep, part
|
|||||||
|
|
||||||
% Define domain
|
% Define domain
|
||||||
obj.domain = domain;
|
obj.domain = domain;
|
||||||
obj.partitioningFreq = partitoningFreq;
|
|
||||||
|
|
||||||
% Add geometries representing obstacles within the domain
|
% Add geometries representing obstacles within the domain
|
||||||
obj.obstacles = obstacles;
|
obj.obstacles = obstacles;
|
||||||
|
|
||||||
% Add an additional obstacle spanning the domain's footprint to
|
% Add an additional obstacle spanning the domain's footprint to
|
||||||
% represent the minimum allowable altitude
|
% represent the minimum allowable altitude
|
||||||
obj.minAlt = minAlt;
|
if minAlt > 0
|
||||||
if obj.minAlt > 0
|
|
||||||
obj.obstacles{end + 1, 1} = rectangularPrism;
|
obj.obstacles{end + 1, 1} = rectangularPrism;
|
||||||
obj.obstacles{end, 1} = obj.obstacles{end, 1}.initialize([obj.domain.minCorner; obj.domain.maxCorner(1:2), obj.minAlt], "OBSTACLE", "Minimum Altitude Domain Constraint");
|
obj.obstacles{end, 1} = obj.obstacles{end, 1}.initialize([obj.domain.minCorner; obj.domain.maxCorner(1:2), minAlt], "OBSTACLE", "Minimum Altitude Domain Constraint");
|
||||||
end
|
end
|
||||||
|
|
||||||
% Define objective
|
|
||||||
obj.objective = objective;
|
|
||||||
|
|
||||||
% Define agents
|
% Define agents
|
||||||
obj.agents = agents;
|
obj.agents = agents;
|
||||||
obj.constraintAdjacencyMatrix = logical(eye(size(agents, 1)));
|
obj.constraintAdjacencyMatrix = logical(eye(size(agents, 1)));
|
||||||
@@ -67,22 +65,25 @@ function obj = initialize(obj, domain, objective, agents, minAlt, timestep, part
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
% Set CBF parameters
|
||||||
|
obj.barrierGain = barrierGain;
|
||||||
|
obj.barrierExponent = barrierExponent;
|
||||||
|
|
||||||
% Compute adjacency matrix and lesser neighbors
|
% Compute adjacency matrix and lesser neighbors
|
||||||
obj = obj.updateAdjacency();
|
obj = obj.updateAdjacency();
|
||||||
obj = obj.lesserNeighbor();
|
obj = obj.lesserNeighbor();
|
||||||
|
|
||||||
% Set up times to iterate over
|
% Set up times to iterate over
|
||||||
obj.times = linspace(0, obj.timestep * obj.maxIter, obj.maxIter+1)';
|
obj.times = linspace(0, obj.timestep * obj.maxIter, obj.maxIter+1)';
|
||||||
obj.partitioningTimes = obj.times(obj.partitioningFreq:obj.partitioningFreq:size(obj.times, 1));
|
|
||||||
|
|
||||||
% Prepare performance data store (at t = 0, all have 0 performance)
|
% Prepare performance data store (at t = 0, all have 0 performance)
|
||||||
obj.perf = [zeros(size(obj.agents, 1) + 1, 1), NaN(size(obj.agents, 1) + 1, size(obj.partitioningTimes, 1) - 1)];
|
obj.perf = [zeros(size(obj.agents, 1) + 1, 1), NaN(size(obj.agents, 1) + 1, size(obj.partitioningTimes, 1) - 1)];
|
||||||
|
|
||||||
% Prepare h function data store
|
% Prepare h function data store
|
||||||
obj.h = NaN(size(obj.agents, 1) * (size(obj.agents, 1) - 1) / 2 + size(obj.agents, 1) * size(obj.obstacles, 1) + 6, size(obj.times, 1) - 1);
|
obj.h = NaN(size(obj.agents, 1) * (size(obj.agents, 1) - 1) / 2 + size(obj.agents, 1) * size(obj.obstacles, 1) + 6, size(obj.times, 1));
|
||||||
|
|
||||||
% Create initial partitioning
|
% Create initial partitioning
|
||||||
obj = obj.partition();
|
obj.partitioning = obj.agents{1}.partition(obj.agents, obj.domain.objective);
|
||||||
|
|
||||||
% Initialize variable that will store agent positions for trail plots
|
% Initialize variable that will store agent positions for trail plots
|
||||||
obj.posHist = NaN(size(obj.agents, 1), obj.maxIter + 1, 3);
|
obj.posHist = NaN(size(obj.agents, 1), obj.maxIter + 1, 3);
|
||||||
@@ -90,4 +91,7 @@ function obj = initialize(obj, domain, objective, agents, minAlt, timestep, part
|
|||||||
|
|
||||||
% Set up plots showing initialized state
|
% Set up plots showing initialized state
|
||||||
obj = obj.plot();
|
obj = obj.plot();
|
||||||
|
|
||||||
|
% Run validations
|
||||||
|
obj.validate();
|
||||||
end
|
end
|
||||||
@@ -5,7 +5,6 @@ classdef miSim
|
|||||||
properties (SetAccess = private, GetAccess = public)
|
properties (SetAccess = private, GetAccess = public)
|
||||||
timestep = NaN; % delta time interval for simulation iterations
|
timestep = NaN; % delta time interval for simulation iterations
|
||||||
timestepIndex = NaN; % index of the current timestep (useful for time-indexed arrays)
|
timestepIndex = NaN; % index of the current timestep (useful for time-indexed arrays)
|
||||||
partitioningFreq = NaN; % number of simulation timesteps at which the partitioning routine is re-run
|
|
||||||
maxIter = NaN; % maximum number of simulation iterations
|
maxIter = NaN; % maximum number of simulation iterations
|
||||||
domain = rectangularPrism;
|
domain = rectangularPrism;
|
||||||
objective = sensingObjective;
|
objective = sensingObjective;
|
||||||
@@ -13,13 +12,12 @@ classdef miSim
|
|||||||
agents = cell(0, 1); % agents that move within the domain
|
agents = cell(0, 1); % agents that move within the domain
|
||||||
adjacency = NaN; % Adjacency matrix representing communications network graph
|
adjacency = NaN; % Adjacency matrix representing communications network graph
|
||||||
constraintAdjacencyMatrix = NaN; % Adjacency matrix representing desired lesser neighbor connections
|
constraintAdjacencyMatrix = NaN; % Adjacency matrix representing desired lesser neighbor connections
|
||||||
sensorPerformanceMinimum = 1e-6; % minimum sensor performance to allow assignment of a point in the domain to a partition
|
|
||||||
partitioning = NaN;
|
partitioning = NaN;
|
||||||
perf; % sensor performance timeseries array
|
perf; % sensor performance timeseries array
|
||||||
performance = 0; % simulation performance timeseries vector
|
performance = 0; % simulation performance timeseries vector
|
||||||
barrierGain = 100; % collision avoidance parameter
|
barrierGain = 100; % CBF gain parameter
|
||||||
minAlt = 1; % minimum allowed altitude constraint
|
barrierExponent = 3; % CBF exponent parameter
|
||||||
|
artifactName = "";
|
||||||
fPerf; % performance plot figure
|
fPerf; % performance plot figure
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -56,7 +54,7 @@ classdef miSim
|
|||||||
end
|
end
|
||||||
|
|
||||||
methods (Access = public)
|
methods (Access = public)
|
||||||
[obj] = initialize(obj, domain, objective, agents, timestep, partitoningFreq, maxIter, obstacles);
|
[obj] = initialize(obj, domain, agents, barrierGain, barrierExponent, minAlt, timestep, maxIter, obstacles, makePlots, makeVideo);
|
||||||
[obj] = run(obj);
|
[obj] = run(obj);
|
||||||
[obj] = lesserNeighbor(obj);
|
[obj] = lesserNeighbor(obj);
|
||||||
[obj] = constrainMotion(obj);
|
[obj] = constrainMotion(obj);
|
||||||
@@ -68,8 +66,9 @@ classdef miSim
|
|||||||
[obj] = plotGraph(obj);
|
[obj] = plotGraph(obj);
|
||||||
[obj] = plotTrails(obj);
|
[obj] = plotTrails(obj);
|
||||||
[obj] = plotH(obj);
|
[obj] = plotH(obj);
|
||||||
[obj] = updatePlots(obj, updatePartitions);
|
[obj] = updatePlots(obj);
|
||||||
validate(obj);
|
validate(obj);
|
||||||
|
teardown(obj);
|
||||||
end
|
end
|
||||||
methods (Access = private)
|
methods (Access = private)
|
||||||
[v] = setupVideoWriter(obj);
|
[v] = setupVideoWriter(obj);
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
function obj = partition(obj)
|
|
||||||
arguments (Input)
|
|
||||||
obj (1, 1) {mustBeA(obj, 'miSim')};
|
|
||||||
end
|
|
||||||
arguments (Output)
|
|
||||||
obj (1, 1) {mustBeA(obj, 'miSim')};
|
|
||||||
end
|
|
||||||
|
|
||||||
% Assess sensing performance of each agent at each sample point
|
|
||||||
% in the domain
|
|
||||||
agentPerformances = cellfun(@(x) reshape(x.sensorModel.sensorPerformance(x.pos, x.pan, x.tilt, [obj.objective.X(:), obj.objective.Y(:), zeros(size(obj.objective.X(:)))]), size(obj.objective.X)), obj.agents, 'UniformOutput', false);
|
|
||||||
agentPerformances{end + 1} = obj.sensorPerformanceMinimum * ones(size(agentPerformances{end})); % add additional layer to represent the threshold that has to be cleared for assignment to any partiton
|
|
||||||
agentPerformances = cat(3, agentPerformances{:});
|
|
||||||
|
|
||||||
% Get highest performance value at each point
|
|
||||||
[~, idx] = max(agentPerformances, [], 3);
|
|
||||||
|
|
||||||
% Collect agent indices in the same way as performance
|
|
||||||
indices = 1:size(obj.agents, 1);
|
|
||||||
agentInds = squeeze(tensorprod(indices, ones(size(obj.objective.X))));
|
|
||||||
if size(agentInds, 1) ~= size(obj.agents, 1)
|
|
||||||
agentInds = reshape(agentInds, [size(obj.agents, 1), size(agentInds)]); % needed for cases with 1 agent where prior squeeze is too agressive
|
|
||||||
end
|
|
||||||
agentInds = num2cell(agentInds, 2:3);
|
|
||||||
agentInds = cellfun(@(x) squeeze(x), agentInds, 'UniformOutput', false);
|
|
||||||
agentInds{end + 1} = zeros(size(agentInds{end})); % index for no assignment
|
|
||||||
agentInds = cat(3, agentInds{:});
|
|
||||||
|
|
||||||
% Use highest performing agent's index to form partitions
|
|
||||||
[m, n, ~] = size(agentInds);
|
|
||||||
[jj, kk] = ndgrid(1:m, 1:n);
|
|
||||||
obj.partitioning = agentInds(sub2ind(size(agentInds), jj, kk, idx));
|
|
||||||
end
|
|
||||||
@@ -51,4 +51,7 @@ function obj = plot(obj)
|
|||||||
|
|
||||||
% Plot h functions
|
% Plot h functions
|
||||||
obj = obj.plotH();
|
obj = obj.plotH();
|
||||||
|
|
||||||
|
% Switch back to primary figure
|
||||||
|
figure(obj.f);
|
||||||
end
|
end
|
||||||
20
@miSim/run.m
20
@miSim/run.m
@@ -18,41 +18,39 @@ function [obj] = run(obj)
|
|||||||
obj.timestepIndex = ii;
|
obj.timestepIndex = ii;
|
||||||
fprintf("Sim Time: %4.2f (%d/%d)\n", obj.t, ii, obj.maxIter + 1);
|
fprintf("Sim Time: %4.2f (%d/%d)\n", obj.t, ii, obj.maxIter + 1);
|
||||||
|
|
||||||
|
% Before moving
|
||||||
% Validate current simulation configuration
|
% Validate current simulation configuration
|
||||||
obj.validate();
|
obj.validate();
|
||||||
|
|
||||||
% Check if it's time for new partitions
|
% Update partitioning before moving (this one is strictly for
|
||||||
updatePartitions = false;
|
% plotting purposes, the real partitioning is done by the agents)
|
||||||
if ismember(obj.t, obj.partitioningTimes)
|
obj.partitioning = obj.agents{1}.partition(obj.agents, obj.domain.objective);
|
||||||
updatePartitions = true;
|
|
||||||
obj = obj.partition();
|
|
||||||
end
|
|
||||||
|
|
||||||
% Determine desired communications links
|
% Determine desired communications links
|
||||||
obj = obj.lesserNeighbor();
|
obj = obj.lesserNeighbor();
|
||||||
|
|
||||||
|
% Moving
|
||||||
% Iterate over agents to simulate their unconstrained motion
|
% Iterate over agents to simulate their unconstrained motion
|
||||||
for jj = 1:size(obj.agents, 1)
|
for jj = 1:size(obj.agents, 1)
|
||||||
obj.agents{jj} = obj.agents{jj}.run(obj.domain, obj.partitioning, obj.t, jj);
|
obj.agents{jj} = obj.agents{jj}.run(obj.domain, obj.partitioning, obj.timestepIndex, jj, obj.agents);
|
||||||
end
|
end
|
||||||
|
|
||||||
% Adjust motion determined by unconstrained gradient ascent using
|
% Adjust motion determined by unconstrained gradient ascent using
|
||||||
% CBF constraints solved by QP
|
% CBF constraints solved by QP
|
||||||
obj = constrainMotion(obj);
|
obj = constrainMotion(obj);
|
||||||
|
|
||||||
% Finished simulation for this timestep, do accounting
|
% After moving
|
||||||
|
|
||||||
% Update agent position history array
|
% Update agent position history array
|
||||||
obj.posHist(1:size(obj.agents, 1), obj.timestepIndex + 1, 1:3) = reshape(cell2mat(cellfun(@(x) x.pos, obj.agents, 'UniformOutput', false)), size(obj.agents, 1), 1, 3);
|
obj.posHist(1:size(obj.agents, 1), obj.timestepIndex + 1, 1:3) = reshape(cell2mat(cellfun(@(x) x.pos, obj.agents, 'UniformOutput', false)), size(obj.agents, 1), 1, 3);
|
||||||
|
|
||||||
% Update total performance
|
% Update total performance
|
||||||
obj.performance = [obj.performance, sum(cellfun(@(x) x.performance(end), obj.agents))];
|
obj.performance = [obj.performance, sum(cellfun(@(x) x.performance(obj.timestepIndex+1), obj.agents))];
|
||||||
|
|
||||||
% Update adjacency matrix
|
% Update adjacency matrix
|
||||||
obj = obj.updateAdjacency();
|
obj = obj.updateAdjacency();
|
||||||
|
|
||||||
% Update plots
|
% Update plots
|
||||||
obj = obj.updatePlots(updatePartitions);
|
obj = obj.updatePlots();
|
||||||
|
|
||||||
% Write frame in to video
|
% Write frame in to video
|
||||||
if obj.makeVideo
|
if obj.makeVideo
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ function v = setupVideoWriter(obj)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if ispc || ismac
|
if ispc || ismac
|
||||||
v = VideoWriter(fullfile('sandbox', strcat(string(datetime('now'), 'yyyy_MM_dd_HH_mm_ss'), '_miSimHist')), 'MPEG-4');
|
v = VideoWriter(fullfile(matlab.project.rootProject().RootFolder, 'sandbox', strcat(obj.artifactName, "_miSimHist")), 'MPEG-4');
|
||||||
elseif isunix
|
elseif isunix
|
||||||
v = VideoWriter(fullfile('.', strcat(string(datetime('now'), 'yyyy_MM_dd_HH_mm_ss'), '_miSimHist')), 'Motion JPEG AVI');
|
v = VideoWriter(fullfile(matlab.project.rootProject().RootFolder, 'sandbox', strcat(obj.artifactName, "_miSimHist")), 'Motion JPEG AVI');
|
||||||
end
|
end
|
||||||
|
|
||||||
v.FrameRate = 1 / obj.timestep;
|
v.FrameRate = 1 / obj.timestep;
|
||||||
|
|||||||
13
@miSim/teardown.m
Normal file
13
@miSim/teardown.m
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
function teardown(obj)
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'miSim')};
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
end
|
||||||
|
|
||||||
|
% Close plots
|
||||||
|
close(obj.hf);
|
||||||
|
close(obj.fPerf);
|
||||||
|
close(obj.f);
|
||||||
|
|
||||||
|
end
|
||||||
@@ -17,19 +17,8 @@ function obj = updateAdjacency(obj)
|
|||||||
A(ii, jj) = false; % comm range violation
|
A(ii, jj) = false; % comm range violation
|
||||||
continue;
|
continue;
|
||||||
end
|
end
|
||||||
|
|
||||||
% % Check that agents do not have their line of sight obstructed
|
|
||||||
% for kk = 1:size(obj.obstacles, 1)
|
|
||||||
% if obj.obstacles{kk}.containsLine(obj.agents{jj}.pos, obj.agents{ii}.pos)
|
|
||||||
% A(ii, jj) = false;
|
|
||||||
% end
|
|
||||||
% end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
obj.adjacency = A & A';
|
obj.adjacency = A & A';
|
||||||
|
|
||||||
if any(obj.adjacency - obj.constraintAdjacencyMatrix < 0, 'all')
|
|
||||||
warning("Eliminated network connections that were necessary");
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
function [obj] = updatePlots(obj, updatePartitions)
|
function [obj] = updatePlots(obj)
|
||||||
arguments (Input)
|
arguments (Input)
|
||||||
obj (1, 1) {mustBeA(obj, 'miSim')};
|
obj (1, 1) {mustBeA(obj, 'miSim')};
|
||||||
updatePartitions (1, 1) logical = false;
|
|
||||||
end
|
end
|
||||||
arguments (Output)
|
arguments (Output)
|
||||||
obj (1, 1) {mustBeA(obj, 'miSim')};
|
obj (1, 1) {mustBeA(obj, 'miSim')};
|
||||||
@@ -30,10 +29,8 @@ function [obj] = updatePlots(obj, updatePartitions)
|
|||||||
obj = obj.plotGraph();
|
obj = obj.plotGraph();
|
||||||
|
|
||||||
% Update partitioning plot
|
% Update partitioning plot
|
||||||
if updatePartitions
|
|
||||||
delete(obj.partitionPlot);
|
delete(obj.partitionPlot);
|
||||||
obj = obj.plotPartitions();
|
obj = obj.plotPartitions();
|
||||||
end
|
|
||||||
|
|
||||||
% reset plot limits to fit domain
|
% reset plot limits to fit domain
|
||||||
for ii = 1:size(obj.spatialPlotIndices, 2)
|
for ii = 1:size(obj.spatialPlotIndices, 2)
|
||||||
@@ -53,12 +50,12 @@ function [obj] = updatePlots(obj, updatePartitions)
|
|||||||
|
|
||||||
% Update performance plot
|
% Update performance plot
|
||||||
% Re-normalize performance plot
|
% Re-normalize performance plot
|
||||||
normalizingFactor = 1/max(obj.performance(end));
|
normalizingFactor = 1/max(obj.performance);
|
||||||
obj.performancePlot(1).YData(1:length(obj.performance)) = obj.performance * normalizingFactor;
|
obj.performancePlot(1).YData(1:(length(obj.performance) + 1)) = [obj.performance, 0] * normalizingFactor;
|
||||||
obj.performancePlot(1).XData(obj.timestepIndex) = obj.t;
|
obj.performancePlot(1).XData([obj.timestepIndex, obj.timestepIndex + 1]) = [obj.t, obj.t + obj.timestep];
|
||||||
for ii = 2:(size(obj.agents, 1) + 1)
|
for ii = 1:(size(obj.agents, 1))
|
||||||
obj.performancePlot(ii).YData(1:length(obj.performance)) = obj.agents{ii - 1}.performance * normalizingFactor;
|
obj.performancePlot(ii + 1).YData(1:(length(obj.performance) + 1)) = [obj.agents{ii}.performance(1:length(obj.performance)), 0] * normalizingFactor;
|
||||||
obj.performancePlot(ii).XData(obj.timestepIndex) = obj.t;
|
obj.performancePlot(ii + 1).XData([obj.timestepIndex, obj.timestepIndex + 1]) = [obj.t, obj.t + obj.timestep];
|
||||||
end
|
end
|
||||||
|
|
||||||
% Update h function plots
|
% Update h function plots
|
||||||
|
|||||||
@@ -5,8 +5,23 @@ function validate(obj)
|
|||||||
arguments (Output)
|
arguments (Output)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
%% Communications Network Validators
|
||||||
if max(conncomp(graph(obj.adjacency))) ~= 1
|
if max(conncomp(graph(obj.adjacency))) ~= 1
|
||||||
warning("Network is not connected");
|
warning("Network is not connected");
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if any(obj.adjacency - obj.constraintAdjacencyMatrix < 0, 'all')
|
||||||
|
warning("Eliminated network connections that were necessary");
|
||||||
|
end
|
||||||
|
|
||||||
|
%% Obstacle Validators
|
||||||
|
AO_collisions = cellfun(@(a) cellfun(@(o) o.contains(a.pos), obj.obstacles), obj.agents, 'UniformOutput', false);
|
||||||
|
AO_collisions = vertcat(AO_collisions{:});
|
||||||
|
if any(AO_collisions)
|
||||||
|
[idx, idy] = find(AO_collisions);
|
||||||
|
for ii = 1:size(idx, 1)
|
||||||
|
error("Agent(s) %d colliding with obstacle(s) %d", idx(ii), idy(ii));
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
25
@miSim/writeParams.m
Normal file
25
@miSim/writeParams.m
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
function writeParams(obj)
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'miSim')};
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
end
|
||||||
|
|
||||||
|
% Collect agent parameters
|
||||||
|
collisionRadii = cellfun(@(x) x.collisionGeometry.radius, obj.agents);
|
||||||
|
alphaDist = cellfun(@(x) x.sensorModel.alphaDist, obj.agents);
|
||||||
|
betaDist = cellfun(@(x) x.sensorModel.betaDist, obj.agents);
|
||||||
|
alphaTilt = cellfun(@(x) x.sensorModel.alphaTilt, obj.agents);
|
||||||
|
betaTilt = cellfun(@(x) x.sensorModel.alphaDist, obj.agents);
|
||||||
|
comRange = cellfun(@(x) x.commsGeometry.radius, obj.agents);
|
||||||
|
|
||||||
|
% Combine with simulation parameters
|
||||||
|
params = struct('timestep', obj.timestep, 'maxIter', obj.maxIter, 'minAlt', obj.obstacles{end}.maxCorner(3), 'discretizationStep', obj.domain.objective.discretizationStep, ...
|
||||||
|
'collisionRadius', collisionRadii, 'alphaDist', alphaDist, 'betaDist', betaDist, ...
|
||||||
|
'alphaTilt', alphaTilt, 'betaTilt', betaTilt, 'comRange', comRange);
|
||||||
|
|
||||||
|
% Save all parameters to output file
|
||||||
|
paramsFile = strcat(obj.artifactName, "_miSimParams");
|
||||||
|
paramsFile = fullfile(matlab.project.rootProject().RootFolder, 'sandbox', paramsFile);
|
||||||
|
save(paramsFile, "-struct", "params");
|
||||||
|
end
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
function obj = initialize(obj, objectiveFunction, domain, discretizationStep, protectedRange)
|
function obj = initialize(obj, objectiveFunction, domain, discretizationStep, protectedRange, sensorPerformanceMinimum)
|
||||||
arguments (Input)
|
arguments (Input)
|
||||||
obj (1,1) {mustBeA(obj, 'sensingObjective')};
|
obj (1,1) {mustBeA(obj, 'sensingObjective')};
|
||||||
objectiveFunction (1, 1) {mustBeA(objectiveFunction, 'function_handle')};
|
objectiveFunction (1, 1) {mustBeA(objectiveFunction, 'function_handle')};
|
||||||
domain (1, 1) {mustBeGeometry};
|
domain (1, 1) {mustBeGeometry};
|
||||||
discretizationStep (1, 1) double = 1;
|
discretizationStep (1, 1) double = 1;
|
||||||
protectedRange (1, 1) double = 1;
|
protectedRange (1, 1) double = 1;
|
||||||
|
sensorPerformanceMinimum (1, 1) double = 1e-6;
|
||||||
end
|
end
|
||||||
arguments (Output)
|
arguments (Output)
|
||||||
obj (1,1) {mustBeA(obj, 'sensingObjective')};
|
obj (1,1) {mustBeA(obj, 'sensingObjective')};
|
||||||
@@ -12,6 +13,8 @@ function obj = initialize(obj, objectiveFunction, domain, discretizationStep, pr
|
|||||||
|
|
||||||
obj.discretizationStep = discretizationStep;
|
obj.discretizationStep = discretizationStep;
|
||||||
|
|
||||||
|
obj.sensorPerformanceMinimum = sensorPerformanceMinimum;
|
||||||
|
|
||||||
obj.groundAlt = domain.minCorner(3);
|
obj.groundAlt = domain.minCorner(3);
|
||||||
obj.protectedRange = protectedRange;
|
obj.protectedRange = protectedRange;
|
||||||
|
|
||||||
|
|||||||
@@ -2,18 +2,19 @@ classdef sensingObjective
|
|||||||
% Sensing objective definition parent class
|
% Sensing objective definition parent class
|
||||||
properties (SetAccess = private, GetAccess = public)
|
properties (SetAccess = private, GetAccess = public)
|
||||||
label = "";
|
label = "";
|
||||||
groundAlt = 0;
|
groundAlt = NaN;
|
||||||
groundPos = [0, 0];
|
groundPos = [NaN, NaN];
|
||||||
discretizationStep = 1;
|
discretizationStep = NaN;
|
||||||
objectiveFunction = @(x, y) 0; % define objective functions over a grid in this manner
|
objectiveFunction = @(x, y) NaN; % define objective functions over a grid in this manner
|
||||||
X = [];
|
X = [];
|
||||||
Y = [];
|
Y = [];
|
||||||
values = [];
|
values = [];
|
||||||
protectedRange = 1; % keep obstacles from crowding objective
|
protectedRange = NaN; % keep obstacles from crowding objective
|
||||||
|
sensorPerformanceMinimum = NaN; % minimum sensor performance to allow assignment of a point in the domain to a partition
|
||||||
end
|
end
|
||||||
|
|
||||||
methods (Access = public)
|
methods (Access = public)
|
||||||
[obj] = initialize(obj, objectiveFunction, domain, discretizationStep, protectedRange);
|
[obj] = initialize(obj, objectiveFunction, domain, discretizationStep, protectedRange, sensorPerformanceMinimum);
|
||||||
[obj] = initializeRandomMvnpdf(obj, domain, protectedRange, discretizationStep, protectedRange);
|
[obj] = initializeRandomMvnpdf(obj, domain, protectedRange, discretizationStep, protectedRange);
|
||||||
[f ] = plot(obj, ind, f);
|
[f ] = plot(obj, ind, f);
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
function obj = initialize(obj, alphaDist, betaDist, alphaPan, betaPan, alphaTilt, betaTilt)
|
function obj = initialize(obj, alphaDist, betaDist, alphaTilt, betaTilt)
|
||||||
arguments (Input)
|
arguments (Input)
|
||||||
obj (1, 1) {mustBeA(obj, 'sigmoidSensor')}
|
obj (1, 1) {mustBeA(obj, 'sigmoidSensor')}
|
||||||
alphaDist (1, 1) double;
|
alphaDist (1, 1) double;
|
||||||
betaDist (1, 1) double;
|
betaDist (1, 1) double;
|
||||||
alphaPan (1, 1) double;
|
|
||||||
betaPan (1, 1) double;
|
|
||||||
alphaTilt (1, 1) double;
|
alphaTilt (1, 1) double;
|
||||||
betaTilt (1, 1) double;
|
betaTilt (1, 1) double;
|
||||||
end
|
end
|
||||||
@@ -14,8 +12,6 @@ function obj = initialize(obj, alphaDist, betaDist, alphaPan, betaPan, alphaTilt
|
|||||||
|
|
||||||
obj.alphaDist = alphaDist;
|
obj.alphaDist = alphaDist;
|
||||||
obj.betaDist = betaDist;
|
obj.betaDist = betaDist;
|
||||||
obj.alphaPan = alphaPan;
|
|
||||||
obj.betaPan = betaPan;
|
|
||||||
obj.alphaTilt = alphaTilt;
|
obj.alphaTilt = alphaTilt;
|
||||||
obj.betaTilt = betaTilt;
|
obj.betaTilt = betaTilt;
|
||||||
end
|
end
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
function value = sensorPerformance(obj, agentPos, agentPan, agentTilt, targetPos)
|
function value = sensorPerformance(obj, agentPos, targetPos)
|
||||||
arguments (Input)
|
arguments (Input)
|
||||||
obj (1, 1) {mustBeA(obj, 'sigmoidSensor')};
|
obj (1, 1) {mustBeA(obj, 'sigmoidSensor')};
|
||||||
agentPos (1, 3) double;
|
agentPos (1, 3) double;
|
||||||
agentPan (1, 1) double;
|
|
||||||
agentTilt (1, 1) double;
|
|
||||||
targetPos (:, 3) double;
|
targetPos (:, 3) double;
|
||||||
end
|
end
|
||||||
arguments (Output)
|
arguments (Output)
|
||||||
@@ -15,7 +13,7 @@ function value = sensorPerformance(obj, agentPos, agentPan, agentTilt, targetPos
|
|||||||
x = vecnorm(agentPos(1:2) - targetPos(:, 1:2), 2, 2); % distance from sensor nadir to target nadir (i.e. distance ignoring height difference)
|
x = vecnorm(agentPos(1:2) - targetPos(:, 1:2), 2, 2); % distance from sensor nadir to target nadir (i.e. distance ignoring height difference)
|
||||||
|
|
||||||
% compute tilt angle
|
% compute tilt angle
|
||||||
tiltAngle = (180 - atan2d(x, targetPos(:, 3) - agentPos(3))) - agentTilt; % degrees
|
tiltAngle = (180 - atan2d(x, targetPos(:, 3) - agentPos(3))); % degrees
|
||||||
|
|
||||||
% Membership functions
|
% Membership functions
|
||||||
mu_d = obj.distanceMembership(d);
|
mu_d = obj.distanceMembership(d);
|
||||||
@@ -3,15 +3,12 @@ classdef sigmoidSensor
|
|||||||
% Sensor parameters
|
% Sensor parameters
|
||||||
alphaDist = NaN;
|
alphaDist = NaN;
|
||||||
betaDist = NaN;
|
betaDist = NaN;
|
||||||
alphaPan = NaN;
|
|
||||||
betaPan = NaN;
|
|
||||||
alphaTilt = NaN; % degrees
|
alphaTilt = NaN; % degrees
|
||||||
betaTilt = NaN;
|
betaTilt = NaN;
|
||||||
end
|
end
|
||||||
|
|
||||||
methods (Access = public)
|
methods (Access = public)
|
||||||
[obj] = initialize(obj, alphaDist, betaDist, alphaPan, betaPan, alphaTilt, betaTilt);
|
[obj] = initialize(obj, alphaDist, betaDist, alphaTilt, betaTilt);
|
||||||
[values, positions] = sense(obj, agent, sensingObjective, domain, partitioning);
|
|
||||||
[value] = sensorPerformance(obj, agentPos, agentPan, agentTilt, targetPos);
|
[value] = sensorPerformance(obj, agentPos, agentPan, agentTilt, targetPos);
|
||||||
[f] = plotParameters(obj);
|
[f] = plotParameters(obj);
|
||||||
end
|
end
|
||||||
@@ -17,6 +17,6 @@ classdef cone
|
|||||||
|
|
||||||
methods (Access = public)
|
methods (Access = public)
|
||||||
[obj ] = initialize(obj, center, radius, height, tag, label);
|
[obj ] = initialize(obj, center, radius, height, tag, label);
|
||||||
[obj, f] = plot(obj, ind, f);
|
[obj, f] = plot(obj, ind, f, maxAlt);
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
function [obj, f] = plot(obj, ind, f)
|
function [obj, f] = plot(obj, ind, f, maxAlt)
|
||||||
arguments (Input)
|
arguments (Input)
|
||||||
obj (1, 1) {mustBeA(obj, 'cone')};
|
obj (1, 1) {mustBeA(obj, 'cone')};
|
||||||
ind (1, :) double = NaN;
|
ind (1, :) double = NaN;
|
||||||
f (1, 1) {mustBeA(f, 'matlab.ui.Figure')} = figure;
|
f (1, 1) {mustBeA(f, 'matlab.ui.Figure')} = figure;
|
||||||
|
maxAlt (1, 1) = 10;
|
||||||
end
|
end
|
||||||
arguments (Output)
|
arguments (Output)
|
||||||
obj (1, 1) {mustBeA(obj, 'cone')};
|
obj (1, 1) {mustBeA(obj, 'cone')};
|
||||||
@@ -12,16 +13,18 @@ function [obj, f] = plot(obj, ind, f)
|
|||||||
% Create axes if they don't already exist
|
% Create axes if they don't already exist
|
||||||
f = firstPlotSetup(f);
|
f = firstPlotSetup(f);
|
||||||
|
|
||||||
|
scalingFactor = (maxAlt / obj.height);
|
||||||
|
|
||||||
% Plot cone
|
% Plot cone
|
||||||
[X, Y, Z] = cylinder([obj.radius, 0], obj.n);
|
[X, Y, Z] = cylinder([scalingFactor * obj.radius, 0], obj.n);
|
||||||
|
|
||||||
% Scale to match height
|
% Scale to match height
|
||||||
Z = Z * obj.height;
|
Z = Z * maxAlt;
|
||||||
|
|
||||||
% Move to center location
|
% Move to center location
|
||||||
X = X + obj.center(1);
|
X = X + obj.center(1);
|
||||||
Y = Y + obj.center(2);
|
Y = Y + obj.center(2);
|
||||||
Z = Z + obj.center(3);
|
Z = Z + obj.center(3) - maxAlt;
|
||||||
|
|
||||||
% Plot
|
% Plot
|
||||||
if isnan(ind)
|
if isnan(ind)
|
||||||
|
|||||||
@@ -48,13 +48,4 @@ function obj = initialize(obj, bounds, tag, label, objectiveFunction, discretiza
|
|||||||
if tag == REGION_TYPE.DOMAIN
|
if tag == REGION_TYPE.DOMAIN
|
||||||
obj.objective = sensingObjective;
|
obj.objective = sensingObjective;
|
||||||
end
|
end
|
||||||
|
|
||||||
% Initialize CBF
|
|
||||||
% first part evaluates to +/-1 if the point is outside/inside the collision geometry
|
|
||||||
% Second part determines the distance from the point to the boundary of the collision geometry
|
|
||||||
obj.barrierFunction = @(x) (1 - 2 * obj.collisionGeometry.contains(x)) * obj.collisionGeometry.distance(x); % x is 1x3
|
|
||||||
% gradient of barrier function
|
|
||||||
obj.dBarrierFunction = @(x) obj.collisionGeometry.distanceGradient(x); % x is 1x3
|
|
||||||
% as long as the collisionGeometry object is updated during runtime,
|
|
||||||
% these functions never have to be updated again
|
|
||||||
end
|
end
|
||||||
@@ -6,7 +6,7 @@ function [obj] = initializeRandom(obj, tag, label, minDimension, maxDimension, d
|
|||||||
minDimension (1, 1) double = 10;
|
minDimension (1, 1) double = 10;
|
||||||
maxDimension (1, 1) double = 20;
|
maxDimension (1, 1) double = 20;
|
||||||
domain (1, 1) {mustBeGeometry} = rectangularPrism;
|
domain (1, 1) {mustBeGeometry} = rectangularPrism;
|
||||||
minAlt (1, 1) double = 0;
|
minAlt (1, 1) double = 1;
|
||||||
end
|
end
|
||||||
arguments (Output)
|
arguments (Output)
|
||||||
obj (1, 1) {mustBeA(obj, 'rectangularPrism')};
|
obj (1, 1) {mustBeA(obj, 'rectangularPrism')};
|
||||||
|
|||||||
@@ -20,10 +20,6 @@ classdef rectangularPrism
|
|||||||
|
|
||||||
% Plotting
|
% Plotting
|
||||||
lines;
|
lines;
|
||||||
|
|
||||||
% collision
|
|
||||||
barrierFunction;
|
|
||||||
dBarrierFunction;
|
|
||||||
end
|
end
|
||||||
properties (SetAccess = public, GetAccess = public)
|
properties (SetAccess = public, GetAccess = public)
|
||||||
label = "";
|
label = "";
|
||||||
|
|||||||
@@ -18,11 +18,6 @@ function obj = initialize(obj, center, radius, tag, label)
|
|||||||
obj.radius = radius;
|
obj.radius = radius;
|
||||||
obj.diameter = 2 * obj.radius;
|
obj.diameter = 2 * obj.radius;
|
||||||
|
|
||||||
% Initialize CBF
|
|
||||||
obj.barrierFunction = @(x) NaN;
|
|
||||||
% gradient of barrier function
|
|
||||||
obj.dBarrierFunction = @(x) NaN;
|
|
||||||
|
|
||||||
% fake vertices in a cross pattern
|
% fake vertices in a cross pattern
|
||||||
obj.vertices = [obj.center + [obj.radius, 0, 0]; ...
|
obj.vertices = [obj.center + [obj.radius, 0, 0]; ...
|
||||||
obj.center - [obj.radius, 0, 0]; ...
|
obj.center - [obj.radius, 0, 0]; ...
|
||||||
|
|||||||
@@ -11,10 +11,6 @@ classdef spherical
|
|||||||
|
|
||||||
% Plotting
|
% Plotting
|
||||||
lines;
|
lines;
|
||||||
|
|
||||||
% collision
|
|
||||||
barrierFunction;
|
|
||||||
dBarrierFunction;
|
|
||||||
end
|
end
|
||||||
properties (SetAccess = public, GetAccess = public)
|
properties (SetAccess = public, GetAccess = public)
|
||||||
% Meta
|
% Meta
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info location="@fixedCardinalSensor" type="File"/>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info/>
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info location="objectiveFunctionWrapper.m" type="File"/>
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info Ref="sensorModels" Type="Relative"/>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info location="d143c27d-6824-4569-9093-8150b60976cb" type="Reference"/>
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info>
|
||||||
|
<Category UUID="FileClassCategory">
|
||||||
|
<Label UUID="test"/>
|
||||||
|
</Category>
|
||||||
|
</Info>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info location="parametricTestSuite.m" type="File"/>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info location="writeParams.m" type="File"/>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info location="teardown.m" type="File"/>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info location="validate.m" type="File"/>
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Info/>
|
<Info location="plotH.m" type="File"/>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info>
|
||||||
|
<Category UUID="FileClassCategory">
|
||||||
|
<Label UUID="design"/>
|
||||||
|
</Category>
|
||||||
|
</Info>
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info location="sensorModels" type="File"/>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info location="fixedCardinalSensor.m" type="File"/>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info location="sense.m" type="File"/>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info/>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info location="1" type="DIR_SIGNIFIER"/>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info/>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info location="1" type="DIR_SIGNIFIER"/>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info location="sensorPerformance.m" type="File"/>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info location="initialize.m" type="File"/>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
classdef fixedCardinalSensor
|
|
||||||
% Senses in the +/-x, +/- y directions at some specified fixed length
|
|
||||||
properties
|
|
||||||
alphaTilt = NaN;
|
|
||||||
r = 0.1; % fixed sensing length
|
|
||||||
end
|
|
||||||
|
|
||||||
methods (Access = public)
|
|
||||||
[obj] = initialize(obj, r);
|
|
||||||
[neighborValues, neighborPos] = sense(obj, agent, sensingObjective, domain, partitioning);
|
|
||||||
[value] = sensorPerformance(obj, agentPos, agentPan, agentTilt, targetPos);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
function obj = initialize(obj, r)
|
|
||||||
arguments(Input)
|
|
||||||
obj (1, 1) {mustBeA(obj, 'fixedCardinalSensor')};
|
|
||||||
r (1, 1) double;
|
|
||||||
end
|
|
||||||
arguments(Output)
|
|
||||||
obj (1, 1) {mustBeA(obj, 'fixedCardinalSensor')};
|
|
||||||
end
|
|
||||||
obj.r = r;
|
|
||||||
end
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
function [neighborValues, neighborPos] = sense(obj, agent, sensingObjective, domain, partitioning)
|
|
||||||
arguments (Input)
|
|
||||||
obj (1, 1) {mustBeA(obj, 'fixedCardinalSensor')};
|
|
||||||
agent (1, 1) {mustBeA(agent, 'agent')};
|
|
||||||
sensingObjective (1, 1) {mustBeA(sensingObjective, 'sensingObjective')};
|
|
||||||
domain (1, 1) {mustBeGeometry};
|
|
||||||
partitioning (:, :) double = NaN;
|
|
||||||
end
|
|
||||||
arguments (Output)
|
|
||||||
neighborValues (4, 1) double;
|
|
||||||
neighborPos (4, 3) double;
|
|
||||||
end
|
|
||||||
|
|
||||||
% Set alphaTilt to produce an FOV cone with radius 'r' on the ground
|
|
||||||
obj.alphaTilt = atan2(obj.r, agent.pos(3));
|
|
||||||
|
|
||||||
% Evaluate objective at position offsets +/-[r, 0, 0] and +/-[0, r, 0]
|
|
||||||
currentPos = agent.pos(1:2);
|
|
||||||
neighborPos = [currentPos(1) + obj.r, currentPos(2); ... % (+x)
|
|
||||||
currentPos(1), currentPos(2) + obj.r; ... % (+y)
|
|
||||||
currentPos(1) - obj.r, currentPos(2); ... % (-x)
|
|
||||||
currentPos(1), currentPos(2) - obj.r; ... % (-y)
|
|
||||||
];
|
|
||||||
|
|
||||||
% Check for neighbor positions that fall outside of the domain
|
|
||||||
outOfBounds = false(size(neighborPos, 1), 1);
|
|
||||||
for ii = 1:size(neighborPos, 1)
|
|
||||||
if ~domain.contains([neighborPos(ii, :), 0])
|
|
||||||
outOfBounds(ii) = true;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
% Replace out of bounds positions with inoffensive in-bounds positions
|
|
||||||
neighborPos(outOfBounds, 1:3) = repmat(agent.pos, sum(outOfBounds), 1);
|
|
||||||
|
|
||||||
% Sense values at selected positions
|
|
||||||
neighborValues = [sensingObjective.objectiveFunction(neighborPos(1, 1), neighborPos(1, 2)), ... % (+x)
|
|
||||||
sensingObjective.objectiveFunction(neighborPos(2, 1), neighborPos(2, 2)), ... % (+y)
|
|
||||||
sensingObjective.objectiveFunction(neighborPos(3, 1), neighborPos(3, 2)), ... % (-x)
|
|
||||||
sensingObjective.objectiveFunction(neighborPos(4, 1), neighborPos(4, 2)), ... % (-y)
|
|
||||||
];
|
|
||||||
|
|
||||||
% Prevent out of bounds locations from ever possibly being selected
|
|
||||||
neighborValues(outOfBounds) = 0;
|
|
||||||
end
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
function value = sensorPerformance(obj, agentPos, agentPan, agentTilt, targetPos)
|
|
||||||
arguments (Input)
|
|
||||||
obj (1, 1) {mustBeA(obj, 'fixedCardinalSensor')};
|
|
||||||
agentPos (1, 3) double;
|
|
||||||
agentPan (1, 1) double;
|
|
||||||
agentTilt (1, 1) double;
|
|
||||||
targetPos (:, 3) double;
|
|
||||||
end
|
|
||||||
arguments (Output)
|
|
||||||
value (:, 1) double;
|
|
||||||
end
|
|
||||||
|
|
||||||
value = 0.5 * ones(size(targetPos, 1), 1);
|
|
||||||
end
|
|
||||||
77
test/parametricTestSuite.m
Normal file
77
test/parametricTestSuite.m
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
classdef parametricTestSuite < matlab.unittest.TestCase
|
||||||
|
properties (Access = private)
|
||||||
|
% System under test
|
||||||
|
testClass = miSim;
|
||||||
|
domain = rectangularPrism;
|
||||||
|
obstacles = cell(1, 0);
|
||||||
|
|
||||||
|
%% Diagnostic Parameters
|
||||||
|
% No effect on simulation dynamics
|
||||||
|
makeVideo = true; % disable video writing for big performance increase
|
||||||
|
makePlots = true; % disable plotting for big performance increase (also disables video)
|
||||||
|
plotCommsGeometry = false; % disable plotting communications geometries
|
||||||
|
protectedRange = 0;
|
||||||
|
end
|
||||||
|
properties (TestParameter)
|
||||||
|
%% Simulation Parameters
|
||||||
|
timestep = num2cell([1]); % duration of one simulation timestep
|
||||||
|
maxIter = num2cell([25]); % number of timesteps to run
|
||||||
|
|
||||||
|
% Domain parameters
|
||||||
|
minAlt = num2cell([1]); % minimum allowed agent altitude, make sure test cases don't conflict with this
|
||||||
|
|
||||||
|
% Constraint parameters
|
||||||
|
barrierGain = num2cell([100]);
|
||||||
|
barrierExponent = num2cell([3]);
|
||||||
|
|
||||||
|
% Sensing Objective Parameters
|
||||||
|
sensorPerformanceMinimum = num2cell([1e-6]); % sensor performance threshhold for partition assignment
|
||||||
|
discretizationStep = num2cell([0.01]); % sensing objective discretization step size
|
||||||
|
% this value goes on to determine central differences used in
|
||||||
|
% gradient ascent and partitioning element sizes
|
||||||
|
|
||||||
|
% Agent Parameters
|
||||||
|
collisionRadius = num2cell([0.1]);
|
||||||
|
initialStepSize = num2cell([0.2]); % gradient ascent step size at the first iteration. Decreases linearly to 0 based on maxIter.
|
||||||
|
|
||||||
|
% Sensor Model Parameters
|
||||||
|
alphaDist = num2cell([2.5, 5]);
|
||||||
|
betaDist = num2cell([3, 15]);
|
||||||
|
alphaTilt = num2cell([15, 30]); % (degrees)methods
|
||||||
|
betaTilt = num2cell([3, 15]);
|
||||||
|
|
||||||
|
% Communications Parameters
|
||||||
|
comRange = num2cell([3]);
|
||||||
|
end
|
||||||
|
|
||||||
|
methods (Test, ParameterCombination = "exhaustive")
|
||||||
|
% Test cases
|
||||||
|
function single_agent_gradient_ascent(tc, timestep, maxIter, barrierGain, barrierExponent, minAlt, sensorPerformanceMinimum, discretizationStep, collisionRadius, initialStepSize, alphaDist, betaDist, alphaTilt, betaTilt, comRange)
|
||||||
|
% Set up square domain
|
||||||
|
l = 10;
|
||||||
|
tc.domain = tc.domain.initialize([zeros(1, 3); l * ones(1, 3)], REGION_TYPE.DOMAIN, "Domain");
|
||||||
|
tc.domain.objective = tc.domain.objective.initialize(objectiveFunctionWrapper([.75 * l, 0.75 * l]), tc.domain, discretizationStep, tc.protectedRange, sensorPerformanceMinimum);
|
||||||
|
|
||||||
|
% Set up agent
|
||||||
|
sensorModel = sigmoidSensor;
|
||||||
|
sensorModel = sensorModel.initialize(alphaDist, betaDist, alphaTilt, betaTilt);
|
||||||
|
agentPos = [l/4, l/4, l/4];
|
||||||
|
collisionGeometry = spherical;
|
||||||
|
collisionGeometry = collisionGeometry.initialize(agentPos, collisionRadius, REGION_TYPE.COLLISION, "Agent 1 Collision Region");
|
||||||
|
agents = {agent};
|
||||||
|
agents{1} = agents{1}.initialize(agentPos, collisionGeometry, sensorModel, comRange, maxIter, initialStepSize, "Agent 1", tc.plotCommsGeometry);
|
||||||
|
|
||||||
|
% Set up simulation
|
||||||
|
tc.testClass = tc.testClass.initialize(tc.domain, agents, barrierGain, barrierExponent, minAlt, timestep, maxIter, tc.obstacles, tc.makePlots, tc.makeVideo);
|
||||||
|
|
||||||
|
% Save simulation parameters to output file
|
||||||
|
tc.testClass.writeParams();
|
||||||
|
|
||||||
|
% Run
|
||||||
|
tc.testClass = tc.testClass.run();
|
||||||
|
|
||||||
|
% Cleanup
|
||||||
|
tc.testClass.teardown();
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -9,9 +9,8 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
plotCommsGeometry = false; % disable plotting communications geometries
|
plotCommsGeometry = false; % disable plotting communications geometries
|
||||||
|
|
||||||
% Sim
|
% Sim
|
||||||
maxIter = 250;
|
maxIter = 50;
|
||||||
timestep = 0.05
|
timestep = 0.05;
|
||||||
partitoningFreq = 5;
|
|
||||||
|
|
||||||
% Domain
|
% Domain
|
||||||
domain = rectangularPrism; % domain geometry
|
domain = rectangularPrism; % domain geometry
|
||||||
@@ -31,9 +30,9 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
objective = sensingObjective;
|
objective = sensingObjective;
|
||||||
|
|
||||||
% Agents
|
% Agents
|
||||||
minAgents = 4; % Minimum number of agents to be randomly generated
|
initialStepSize = 0.2; % gradient ascent step size at the first iteration. Decreases linearly to 0 based on maxIter.
|
||||||
maxAgents = 6; % Maximum number of agents to be randomly generated
|
minAgents = 3; % Minimum number of agents to be randomly generated
|
||||||
sensingLength = 0.05; % length parameter used by sensing function
|
maxAgents = 4; % Maximum number of agents to be randomly generated
|
||||||
agents = cell(0, 1);
|
agents = cell(0, 1);
|
||||||
|
|
||||||
% Collision
|
% Collision
|
||||||
@@ -53,6 +52,10 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
|
|
||||||
% Communications
|
% Communications
|
||||||
comRange = 8; % Maximum range between agents that forms a communications link
|
comRange = 8; % Maximum range between agents that forms a communications link
|
||||||
|
|
||||||
|
% Constraints
|
||||||
|
barrierGain = 100;
|
||||||
|
barrierExponent = 3;
|
||||||
end
|
end
|
||||||
|
|
||||||
% Setup for each test
|
% Setup for each test
|
||||||
@@ -159,10 +162,10 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
|
|
||||||
% Initialize candidate agent sensor model
|
% Initialize candidate agent sensor model
|
||||||
sensor = sigmoidSensor;
|
sensor = sigmoidSensor;
|
||||||
sensor = sensor.initialize(tc.alphaDistMin + rand * (tc.alphaDistMax - tc.alphaDistMin), tc.betaDistMin + rand * (tc.betaDistMax - tc.betaDistMin), NaN, NaN, tc.alphaTiltMin + rand * (tc.alphaTiltMax - tc.alphaTiltMin), tc.betaTiltMin + rand * (tc.betaTiltMax - tc.betaTiltMin));
|
sensor = sensor.initialize(tc.alphaDistMin + rand * (tc.alphaDistMax - tc.alphaDistMin), tc.betaDistMin + rand * (tc.betaDistMax - tc.betaDistMin), tc.alphaTiltMin + rand * (tc.alphaTiltMax - tc.alphaTiltMin), tc.betaTiltMin + rand * (tc.betaTiltMax - tc.betaTiltMin));
|
||||||
|
|
||||||
% Initialize candidate agent
|
% Initialize candidate agent
|
||||||
newAgent = tc.agents{ii}.initialize(candidatePos, zeros(1,3), 0, 0, candidateGeometry, sensor, tc.comRange);
|
newAgent = tc.agents{ii}.initialize(candidatePos, candidateGeometry, sensor, tc.comRange, tc.maxIter, tc.initialStepSize);
|
||||||
|
|
||||||
% Make sure candidate agent doesn't collide with
|
% Make sure candidate agent doesn't collide with
|
||||||
% domain
|
% domain
|
||||||
@@ -210,7 +213,7 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
% Initialize the simulation
|
% Initialize the simulation
|
||||||
tc.testClass = tc.testClass.initialize(tc.domain, tc.domain.objective, tc.agents, tc.minAlt, tc.timestep, tc.partitoningFreq, tc.maxIter, tc.obstacles, tc.makeVideo);
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, tc.maxIter, tc.obstacles, tc.makeVideo);
|
||||||
end
|
end
|
||||||
function misim_run(tc)
|
function misim_run(tc)
|
||||||
% randomly create obstacles
|
% randomly create obstacles
|
||||||
@@ -293,10 +296,10 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
|
|
||||||
% Initialize candidate agent sensor model
|
% Initialize candidate agent sensor model
|
||||||
sensor = sigmoidSensor;
|
sensor = sigmoidSensor;
|
||||||
sensor = sensor.initialize(tc.alphaDistMin + rand * (tc.alphaDistMax - tc.alphaDistMin), tc.betaDistMin + rand * (tc.betaDistMax - tc.betaDistMin), NaN, NaN, tc.alphaTiltMin + rand * (tc.alphaTiltMax - tc.alphaTiltMin), tc.betaTiltMin + rand * (tc.betaTiltMax - tc.betaTiltMin));
|
sensor = sensor.initialize(tc.alphaDistMin + rand * (tc.alphaDistMax - tc.alphaDistMin), tc.betaDistMin + rand * (tc.betaDistMax - tc.betaDistMin), tc.alphaTiltMin + rand * (tc.alphaTiltMax - tc.alphaTiltMin), tc.betaTiltMin + rand * (tc.betaTiltMax - tc.betaTiltMin));
|
||||||
|
|
||||||
% Initialize candidate agent
|
% Initialize candidate agent
|
||||||
newAgent = tc.agents{ii}.initialize(candidatePos, zeros(1,3), 0, 0, candidateGeometry, sensor, tc.comRange);
|
newAgent = tc.agents{ii}.initialize(candidatePos, candidateGeometry, sensor, tc.comRange, tc.maxIter, tc.initialStepSize);
|
||||||
|
|
||||||
% Make sure candidate agent doesn't collide with
|
% Make sure candidate agent doesn't collide with
|
||||||
% domain
|
% domain
|
||||||
@@ -344,7 +347,10 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
% Initialize the simulation
|
% Initialize the simulation
|
||||||
tc.testClass = tc.testClass.initialize(tc.domain, tc.domain.objective, tc.agents, tc.minAlt, tc.timestep, tc.partitoningFreq, tc.maxIter, tc.obstacles, tc.makeVideo);
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, tc.maxIter, tc.obstacles, tc.makeVideo);
|
||||||
|
|
||||||
|
% Write out parameters
|
||||||
|
tc.testClass.writeParams();
|
||||||
|
|
||||||
% Run simulation loop
|
% Run simulation loop
|
||||||
tc.testClass = tc.testClass.run();
|
tc.testClass = tc.testClass.run();
|
||||||
@@ -369,26 +375,26 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
% Initialize agent sensor model
|
% Initialize agent sensor model
|
||||||
sensor = sigmoidSensor;
|
sensor = sigmoidSensor;
|
||||||
% Homogeneous sensor model parameters
|
% Homogeneous sensor model parameters
|
||||||
sensor = sensor.initialize(2.75, 9, NaN, NaN, 22.5, 9);
|
sensor = sensor.initialize(2.75, 9, 22.5, 9);
|
||||||
% Heterogeneous sensor model parameters
|
% Heterogeneous sensor model parameters
|
||||||
% sensor = sensor.initialize(tc.alphaDistMin + rand * (tc.alphaDistMax - tc.alphaDistMin), tc.betaDistMin + rand * (tc.betaDistMax - tc.betaDistMin), NaN, NaN, tc.alphaTiltMin + rand * (tc.alphaTiltMax - tc.alphaTiltMin), tc.betaTiltMin + rand * (tc.betaTiltMax - tc.betaTiltMin));
|
% sensor = sensor.initialize(tc.alphaDistMin + rand * (tc.alphaDistMax - tc.alphaDistMin), tc.betaDistMin + rand * (tc.betaDistMax - tc.betaDistMin), tc.alphaTiltMin + rand * (tc.alphaTiltMax - tc.alphaTiltMin), tc.betaTiltMin + rand * (tc.betaTiltMax - tc.betaTiltMin));
|
||||||
|
|
||||||
% Plot sensor parameters (optional)
|
% Plot sensor parameters (optional)
|
||||||
% f = sensor.plotParameters();
|
% f = sensor.plotParameters();
|
||||||
|
|
||||||
% Initialize agents
|
% Initialize agents
|
||||||
tc.agents = {agent; agent};
|
tc.agents = {agent; agent};
|
||||||
tc.agents{1} = tc.agents{1}.initialize(tc.domain.center + dh + [d, 0, 0], zeros(1,3), 0, 0, geometry1, sensor, 3*d);
|
tc.agents{1} = tc.agents{1}.initialize(tc.domain.center + dh + [d, 0, 0], geometry1, sensor, 3*d, tc.maxIter, tc.initialStepSize);
|
||||||
tc.agents{2} = tc.agents{2}.initialize(tc.domain.center + dh - [d, 0, 0], zeros(1,3), 0, 0, geometry2, sensor, 3*d);
|
tc.agents{2} = tc.agents{2}.initialize(tc.domain.center + dh - [d, 0, 0], geometry2, sensor, 3*d, tc.maxIter, tc.initialStepSize);
|
||||||
|
|
||||||
% Optional third agent along the +Y axis
|
% Optional third agent along the +Y axis
|
||||||
geometry3 = rectangularPrism;
|
geometry3 = rectangularPrism;
|
||||||
geometry3 = geometry3.initialize([tc.domain.center + dh - [0, d, 0] - tc.collisionRanges(1) * ones(1, 3); tc.domain.center + dh - [0, d, 0] + tc.collisionRanges(1) * ones(1, 3)], REGION_TYPE.COLLISION);
|
geometry3 = geometry3.initialize([tc.domain.center + dh - [0, d, 0] - tc.collisionRanges(1) * ones(1, 3); tc.domain.center + dh - [0, d, 0] + tc.collisionRanges(1) * ones(1, 3)], REGION_TYPE.COLLISION);
|
||||||
tc.agents{3} = agent;
|
tc.agents{3} = agent;
|
||||||
tc.agents{3} = tc.agents{3}.initialize(tc.domain.center + dh - [0, d, 0], zeros(1, 3), 0, 0, geometry3, sensor, 3*d);
|
tc.agents{3} = tc.agents{3}.initialize(tc.domain.center + dh - [0, d, 0], geometry3, sensor, 3*d, tc.maxIter, tc.initialStepSize);
|
||||||
|
|
||||||
% Initialize the simulation
|
% Initialize the simulation
|
||||||
tc.testClass = tc.testClass.initialize(tc.domain, tc.domain.objective, tc.agents, tc.minAlt, tc.timestep, tc.partitoningFreq, tc.maxIter, cell(0, 1), false, false);
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, tc.maxIter, cell(0, 1), false, false);
|
||||||
|
|
||||||
tc.verifyEqual(tc.testClass.partitioning(500, 500:502), [2, 3, 1]); % all three near center
|
tc.verifyEqual(tc.testClass.partitioning(500, 500:502), [2, 3, 1]); % all three near center
|
||||||
tc.verifyLessThan(sum(tc.testClass.partitioning == 1, 'all'), sum(tc.testClass.partitioning == 0, 'all')); % more non-assignments than partition 1 assignments
|
tc.verifyLessThan(sum(tc.testClass.partitioning == 1, 'all'), sum(tc.testClass.partitioning == 0, 'all')); % more non-assignments than partition 1 assignments
|
||||||
@@ -411,58 +417,56 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
% Initialize agent sensor model
|
% Initialize agent sensor model
|
||||||
sensor = sigmoidSensor;
|
sensor = sigmoidSensor;
|
||||||
% Homogeneous sensor model parameters
|
% Homogeneous sensor model parameters
|
||||||
% sensor = sensor.initialize(2.5666, 5.0807, NaN, NaN, 20.8614, 13); % 13
|
% sensor = sensor.initialize(2.5666, 5.0807, 20.8614, 13); % 13
|
||||||
alphaDist = l/2; % half of domain length/width
|
alphaDist = l/2; % half of domain length/width
|
||||||
sensor = sensor.initialize(alphaDist, 3, NaN, NaN, 20, 3);
|
sensor = sensor.initialize(alphaDist, 3, 20, 3);
|
||||||
|
|
||||||
% Plot sensor parameters (optional)
|
% Plot sensor parameters (optional)
|
||||||
% f = sensor.plotParameters();
|
% f = sensor.plotParameters();
|
||||||
|
|
||||||
% Initialize agents
|
% Initialize agents
|
||||||
tc.agents = {agent};
|
tc.agents = {agent};
|
||||||
tc.agents{1} = tc.agents{1}.initialize([tc.domain.center(1:2), 3], zeros(1,3), 0, 0, geometry1, sensor, 3);
|
tc.agents{1} = tc.agents{1}.initialize([tc.domain.center(1:2), 3], geometry1, sensor, 3, tc.maxIter, tc.initialStepSize);
|
||||||
|
|
||||||
% Initialize the simulation
|
% Initialize the simulation
|
||||||
tc.testClass = tc.testClass.initialize(tc.domain, tc.domain.objective, tc.agents, tc.minAlt, tc.timestep, tc.partitoningFreq, tc.maxIter, cell(0, 1), false, false);
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, tc.maxIter, cell(0, 1), false, false);
|
||||||
close(tc.testClass.fPerf);
|
close(tc.testClass.fPerf);
|
||||||
|
|
||||||
tc.verifyEqual(unique(tc.testClass.partitioning), [0; 1]);
|
tc.verifyEqual(unique(tc.testClass.partitioning), [0; 1]);
|
||||||
tc.verifyLessThan(sum(tc.testClass.partitioning == 1, 'all'), sum(tc.testClass.partitioning == 0, 'all'));
|
tc.verifyLessThan(sum(tc.testClass.partitioning == 1, 'all'), sum(tc.testClass.partitioning == 0, 'all'));
|
||||||
end
|
end
|
||||||
function test_single_partition_basic_GA(tc)
|
function test_single_agent_gradient_ascent(tc)
|
||||||
% make basic domain
|
% make basic domain
|
||||||
l = 10; % domain size
|
l = 10; % domain size
|
||||||
tc.domain = tc.domain.initialize([zeros(1, 3); l * ones(1, 3)], REGION_TYPE.DOMAIN, "Domain");
|
tc.domain = tc.domain.initialize([zeros(1, 3); l * ones(1, 3)], REGION_TYPE.DOMAIN, "Domain");
|
||||||
|
|
||||||
% make basic sensing objective
|
% make basic sensing objective
|
||||||
tc.domain.objective = tc.domain.objective.initialize(@(x, y) mvnpdf([x(:), y(:)], [2, 8]), tc.domain, tc.discretizationStep, tc.protectedRange);
|
tc.domain.objective = tc.domain.objective.initialize(@(x, y) mvnpdf([x(:), y(:)], [7, 6]), tc.domain, tc.discretizationStep, tc.protectedRange);
|
||||||
|
|
||||||
% Initialize agent collision geometry
|
% Initialize agent collision geometry
|
||||||
geometry1 = rectangularPrism;
|
geometry1 = rectangularPrism;
|
||||||
geometry1 = geometry1.initialize([[tc.domain.center(1:2)-tc.domain.dimensions(1)/3, 3] - tc.collisionRanges(1) * ones(1, 3); [tc.domain.center(1:2)-tc.domain.dimensions(1)/3, 3] + tc.collisionRanges(1) * ones(1, 3)], REGION_TYPE.COLLISION);
|
geometry1 = geometry1.initialize([[tc.domain.center(1:2)-tc.domain.dimensions(1)/4, 3] - tc.collisionRanges(1) * ones(1, 3); [tc.domain.center(1:2)-tc.domain.dimensions(1)/4, 3] + tc.collisionRanges(1) * ones(1, 3)], REGION_TYPE.COLLISION);
|
||||||
|
|
||||||
% Initialize agent sensor model
|
% Initialize agent sensor model
|
||||||
sensor = sigmoidSensor;
|
sensor = sigmoidSensor;
|
||||||
% Homogeneous sensor model parameters
|
% Homogeneous sensor model parameters
|
||||||
% sensor = sensor.initialize(2.5666, 5.0807, NaN, NaN, 20.8614, 13); % 13
|
% sensor = sensor.initialize(2.5666, 5.0807, 20.8614, 13); % 13
|
||||||
alphaDist = l/2; % half of domain length/width
|
alphaDist = l/2; % half of domain length/width
|
||||||
sensor = sensor.initialize(alphaDist, 3, NaN, NaN, 20, 3);
|
sensor = sensor.initialize(alphaDist, 3, 20, 3);
|
||||||
|
|
||||||
% Plot sensor parameters (optional)
|
% Plot sensor parameters (optional)
|
||||||
% f = sensor.plotParameters();
|
% f = sensor.plotParameters();
|
||||||
|
|
||||||
% Initialize agents
|
% Initialize agents
|
||||||
|
nIter = 75;
|
||||||
tc.agents = {agent};
|
tc.agents = {agent};
|
||||||
tc.agents{1} = tc.agents{1}.initialize([tc.domain.center(1:2)-tc.domain.dimensions(1)/3, 3], zeros(1,3), 0, 0, geometry1, sensor, 3, "", false);
|
tc.agents{1} = tc.agents{1}.initialize([tc.domain.center(1:2)-tc.domain.dimensions(1)/4, 3], geometry1, sensor, 3, nIter, tc.initialStepSize);
|
||||||
|
|
||||||
% Initialize the simulation
|
% Initialize the simulation
|
||||||
tc.testClass = tc.testClass.initialize(tc.domain, tc.domain.objective, tc.agents, tc.minAlt, tc.timestep, tc.partitoningFreq, tc.maxIter, cell(0, 1), true, false);
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, nIter, cell(0, 1));
|
||||||
|
|
||||||
% Run the simulation
|
% Run the simulation
|
||||||
tc.testClass = tc.testClass.run();
|
tc.testClass = tc.testClass.run();
|
||||||
if isgraphics(tc.testClass.agents{1}.debugFig)
|
|
||||||
close(tc.testClass.agents{1}.debugFig);
|
|
||||||
end
|
|
||||||
|
|
||||||
% tc.verifyGreaterThan(tc.testClass.performance(end)/max(tc.testClass.performance), 0.99); % ends up very near a relative maximum
|
% tc.verifyGreaterThan(tc.testClass.performance(end)/max(tc.testClass.performance), 0.99); % ends up very near a relative maximum
|
||||||
end
|
end
|
||||||
@@ -488,24 +492,24 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
% Initialize agent sensor model
|
% Initialize agent sensor model
|
||||||
sensor = sigmoidSensor;
|
sensor = sigmoidSensor;
|
||||||
% Homogeneous sensor model parameters
|
% Homogeneous sensor model parameters
|
||||||
% sensor = sensor.initialize(2.5666, 5.0807, NaN, NaN, 20.8614, 13); % 13
|
% sensor = sensor.initialize(2.5666, 5.0807, 20.8614, 13); % 13
|
||||||
alphaDist = l/2; % half of domain length/width
|
alphaDist = l/2; % half of domain length/width
|
||||||
sensor = sensor.initialize(alphaDist, 3, NaN, NaN, 15, 3);
|
sensor = sensor.initialize(alphaDist, 3, 15, 3);
|
||||||
|
|
||||||
% Initialize agents
|
% Initialize agents
|
||||||
|
nIter = 25;
|
||||||
tc.agents = {agent; agent};
|
tc.agents = {agent; agent};
|
||||||
tc.agents{1} = tc.agents{1}.initialize(tc.domain.center + d, zeros(1,3), 0, 0, geometry1, sensor, 5);
|
tc.agents{1} = tc.agents{1}.initialize(tc.domain.center + d, geometry1, sensor, 5, nIter, tc.initialStepSize);
|
||||||
tc.agents{2} = tc.agents{2}.initialize(tc.domain.center - d, zeros(1,3), 0, 0, geometry2, sensor, 5);
|
tc.agents{2} = tc.agents{2}.initialize(tc.domain.center - d, geometry2, sensor, 5, nIter, tc.initialStepSize);
|
||||||
|
|
||||||
% Initialize the simulation
|
% Initialize the simulation
|
||||||
tc.testClass = tc.testClass.initialize(tc.domain, tc.domain.objective, tc.agents, tc.minAlt, tc.timestep, tc.partitoningFreq, 50, cell(0, 1), tc.makeVideo, tc.makePlots);
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, nIter, cell(0, 1), tc.makeVideo, tc.makePlots);
|
||||||
|
|
||||||
% Run the simulation
|
% Run the simulation
|
||||||
tc.testClass.run();
|
tc.testClass.run();
|
||||||
end
|
end
|
||||||
function test_obstacle_avoidance(tc)
|
function test_obstacle_avoidance(tc)
|
||||||
% Right now this seems to prove that the communications
|
% Right now, the communications constraint is violated here
|
||||||
% constraints are working, but the result is dissatisfying
|
|
||||||
|
|
||||||
% Fixed single obstacle
|
% Fixed single obstacle
|
||||||
% Fixed two agents initial conditions
|
% Fixed two agents initial conditions
|
||||||
@@ -521,7 +525,7 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
radius = 1.1;
|
radius = 1.1;
|
||||||
d = [3, 0, 0];
|
d = [3, 0, 0];
|
||||||
|
|
||||||
yOffset = 0;
|
yOffset = 1;
|
||||||
% choice of 0 leads to the agents getting stuck attempting to go around the obstacle on both sides
|
% choice of 0 leads to the agents getting stuck attempting to go around the obstacle on both sides
|
||||||
% choice of 1 leads to one agent easily going around while the other gets stuck and the communications link is broken
|
% choice of 1 leads to one agent easily going around while the other gets stuck and the communications link is broken
|
||||||
|
|
||||||
@@ -533,7 +537,7 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
% Initialize agent sensor model
|
% Initialize agent sensor model
|
||||||
sensor = sigmoidSensor;
|
sensor = sigmoidSensor;
|
||||||
alphaDist = l/2; % half of domain length/width
|
alphaDist = l/2; % half of domain length/width
|
||||||
sensor = sensor.initialize(alphaDist, 3, NaN, NaN, 15, 3);
|
sensor = sensor.initialize(alphaDist, 3, 15, 3);
|
||||||
|
|
||||||
% Initialize obstacles
|
% Initialize obstacles
|
||||||
obstacleLength = 1;
|
obstacleLength = 1;
|
||||||
@@ -543,11 +547,10 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
% Initialize agents
|
% Initialize agents
|
||||||
commsRadius = (2*radius + obstacleLength) * 0.9; % defined such that they cannot go around the obstacle on both sides
|
commsRadius = (2*radius + obstacleLength) * 0.9; % defined such that they cannot go around the obstacle on both sides
|
||||||
tc.agents = {agent; agent;};
|
tc.agents = {agent; agent;};
|
||||||
tc.agents{1} = tc.agents{1}.initialize(tc.domain.center - d + [0, radius * 1.1 - yOffset, 0], zeros(1,3), 0, 0, geometry1, sensor, commsRadius);
|
tc.agents{1} = tc.agents{1}.initialize(tc.domain.center - d + [0, radius * 1.1 - yOffset, 0], geometry1, sensor, commsRadius, tc.maxIter, tc.initialStepSize);
|
||||||
tc.agents{2} = tc.agents{2}.initialize(tc.domain.center - d - [0, radius *1.1 + yOffset, 0], zeros(1,3), 0, 0, geometry2, sensor, commsRadius);
|
tc.agents{2} = tc.agents{2}.initialize(tc.domain.center - d - [0, radius *1.1 + yOffset, 0], geometry2, sensor, commsRadius, tc.maxIter, tc.initialStepSize);
|
||||||
|
|
||||||
% Initialize the simulation
|
% Initialize the simulation
|
||||||
tc.testClass = tc.testClass.initialize(tc.domain, tc.domain.objective, tc.agents, tc.minAlt, tc.timestep, tc.partitoningFreq, tc.maxIter, tc.obstacles, tc.makeVideo);
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, tc.maxIter, tc.obstacles, tc.makeVideo);
|
||||||
|
|
||||||
% Run the simulation
|
% Run the simulation
|
||||||
tc.testClass.run();
|
tc.testClass.run();
|
||||||
@@ -575,24 +578,25 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
% Initialize agent sensor model
|
% Initialize agent sensor model
|
||||||
sensor = sigmoidSensor;
|
sensor = sigmoidSensor;
|
||||||
alphaDist = l/2; % half of domain length/width
|
alphaDist = l/2; % half of domain length/width
|
||||||
sensor = sensor.initialize(alphaDist, 3, NaN, NaN, 15, 3);
|
sensor = sensor.initialize(alphaDist, 3, 15, 3);
|
||||||
|
|
||||||
% Initialize obstacles
|
% Initialize obstacles
|
||||||
tc.obstacles = {};
|
tc.obstacles = {};
|
||||||
|
|
||||||
% Initialize agents
|
% Initialize agents
|
||||||
|
nIter = 50;
|
||||||
commsRadius = 4; % defined such that they cannot reach their objective without breaking connectivity
|
commsRadius = 4; % defined such that they cannot reach their objective without breaking connectivity
|
||||||
tc.agents = {agent; agent;};
|
tc.agents = {agent; agent;};
|
||||||
tc.agents{1} = tc.agents{1}.initialize(dom.center + d, zeros(1,3), 0, 0, geometry1, sensor, commsRadius);
|
tc.agents{1} = tc.agents{1}.initialize(dom.center + d, geometry1, sensor, commsRadius, nIter, tc.initialStepSize);
|
||||||
tc.agents{2} = tc.agents{2}.initialize(dom.center - d, zeros(1,3), 0, 0, geometry2, sensor, commsRadius);
|
tc.agents{2} = tc.agents{2}.initialize(dom.center - d, geometry2, sensor, commsRadius, nIter, tc.initialStepSize);
|
||||||
|
|
||||||
% Initialize the simulation
|
% Initialize the simulation
|
||||||
tc.testClass = tc.testClass.initialize(dom, dom.objective, tc.agents, tc.minAlt, tc.timestep, tc.partitoningFreq, 75, tc.obstacles, true, false);
|
tc.testClass = tc.testClass.initialize(dom, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, nIter, tc.obstacles, true, false);
|
||||||
|
|
||||||
% Run the simulation
|
% Run the simulation
|
||||||
tc.testClass = tc.testClass.run();
|
tc.testClass = tc.testClass.run();
|
||||||
end
|
end
|
||||||
function test_obstacle_blocks_comms_LOS(tc)
|
function test_obstacle_permits_comms_LOS(tc)
|
||||||
% Fixed single obstacle
|
% Fixed single obstacle
|
||||||
% Fixed two agents initial conditions
|
% Fixed two agents initial conditions
|
||||||
% Exaggerated large communications radius
|
% Exaggerated large communications radius
|
||||||
@@ -614,13 +618,14 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
% Initialize agent sensor model
|
% Initialize agent sensor model
|
||||||
sensor = sigmoidSensor;
|
sensor = sigmoidSensor;
|
||||||
alphaDist = l/2; % half of domain length/width
|
alphaDist = l/2; % half of domain length/width
|
||||||
sensor = sensor.initialize(alphaDist, 3, NaN, NaN, 15, 3);
|
sensor = sensor.initialize(alphaDist, 3, 15, 3);
|
||||||
|
|
||||||
% Initialize agents
|
% Initialize agents
|
||||||
|
nIter = 125;
|
||||||
commsRadius = 5;
|
commsRadius = 5;
|
||||||
tc.agents = {agent; agent;};
|
tc.agents = {agent; agent;};
|
||||||
tc.agents{1} = tc.agents{1}.initialize(tc.domain.center - [d, 0, 0], zeros(1,3), 0, 0, geometry1, sensor, commsRadius);
|
tc.agents{1} = tc.agents{1}.initialize(tc.domain.center - [d, 0, 0], geometry1, sensor, commsRadius, nIter, tc.initialStepSize);
|
||||||
tc.agents{2} = tc.agents{2}.initialize(tc.domain.center - [0, d, 0], zeros(1,3), 0, 0, geometry2, sensor, commsRadius);
|
tc.agents{2} = tc.agents{2}.initialize(tc.domain.center - [0, d, 0], geometry2, sensor, commsRadius, nIter, tc.initialStepSize);
|
||||||
|
|
||||||
% Initialize obstacles
|
% Initialize obstacles
|
||||||
obstacleLength = 1.5;
|
obstacleLength = 1.5;
|
||||||
@@ -628,10 +633,10 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
tc.obstacles{1} = tc.obstacles{1}.initialize([tc.domain.center(1:2) - obstacleLength, 0; tc.domain.center(1:2) + obstacleLength, tc.domain.maxCorner(3)], REGION_TYPE.OBSTACLE, "Obstacle 1");
|
tc.obstacles{1} = tc.obstacles{1}.initialize([tc.domain.center(1:2) - obstacleLength, 0; tc.domain.center(1:2) + obstacleLength, tc.domain.maxCorner(3)], REGION_TYPE.OBSTACLE, "Obstacle 1");
|
||||||
|
|
||||||
% Initialize the simulation
|
% Initialize the simulation
|
||||||
tc.testClass = tc.testClass.initialize(tc.domain, tc.domain.objective, tc.agents, 0, tc.timestep, tc.partitoningFreq, 125, tc.obstacles, false, false);
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, 0, tc.timestep, nIter, tc.obstacles, false, false);
|
||||||
|
|
||||||
% No communications link should be established
|
% No communications link should be established
|
||||||
tc.assertEqual(tc.testClass.adjacency, logical(eye(2)));
|
tc.assertEqual(tc.testClass.adjacency, logical(true(2)));
|
||||||
end
|
end
|
||||||
function test_LNA_case_1(tc)
|
function test_LNA_case_1(tc)
|
||||||
% based on example in meeting
|
% based on example in meeting
|
||||||
@@ -659,19 +664,20 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
% Initialize agent sensor model
|
% Initialize agent sensor model
|
||||||
sensor = sigmoidSensor;
|
sensor = sigmoidSensor;
|
||||||
alphaDist = l/2; % half of domain length/width
|
alphaDist = l/2; % half of domain length/width
|
||||||
sensor = sensor.initialize(alphaDist, 3, NaN, NaN, 15, 3);
|
sensor = sensor.initialize(alphaDist, 3, 15, 3);
|
||||||
|
|
||||||
% Initialize agents
|
% Initialize agents
|
||||||
|
nIter = 125;
|
||||||
commsRadius = d;
|
commsRadius = d;
|
||||||
tc.agents = {agent; agent; agent; agent; agent;};
|
tc.agents = {agent; agent; agent; agent; agent;};
|
||||||
tc.agents{1} = tc.agents{1}.initialize(tc.domain.center + [d, 0, 0], zeros(1,3), 0, 0, geometry1, sensor, commsRadius);
|
tc.agents{1} = tc.agents{1}.initialize(tc.domain.center + [d, 0, 0], geometry1, sensor, commsRadius, nIter, tc.initialStepSize);
|
||||||
tc.agents{2} = tc.agents{2}.initialize(tc.domain.center, zeros(1,3), 0, 0, geometry2, sensor, commsRadius);
|
tc.agents{2} = tc.agents{2}.initialize(tc.domain.center, geometry2, sensor, commsRadius, nIter, tc.initialStepSize);
|
||||||
tc.agents{3} = tc.agents{3}.initialize(tc.domain.center + [-d, d, 0], zeros(1,3), 0, 0, geometry3, sensor, commsRadius);
|
tc.agents{3} = tc.agents{3}.initialize(tc.domain.center + [-d, d, 0], geometry3, sensor, commsRadius, nIter, tc.initialStepSize);
|
||||||
tc.agents{4} = tc.agents{4}.initialize(tc.domain.center + [-2*d, d, 0], zeros(1,3), 0, 0, geometry4, sensor, commsRadius);
|
tc.agents{4} = tc.agents{4}.initialize(tc.domain.center + [-2*d, d, 0], geometry4, sensor, commsRadius, nIter, tc.initialStepSize);
|
||||||
tc.agents{5} = tc.agents{5}.initialize(tc.domain.center + [0, d, 0], zeros(1,3), 0, 0, geometry5, sensor, commsRadius);
|
tc.agents{5} = tc.agents{5}.initialize(tc.domain.center + [0, d, 0], geometry5, sensor, commsRadius, nIter, tc.initialStepSize);
|
||||||
|
|
||||||
% Initialize the simulation
|
% Initialize the simulation
|
||||||
tc.testClass = tc.testClass.initialize(tc.domain, tc.domain.objective, tc.agents, 0, tc.timestep, tc.partitoningFreq, 125, tc.obstacles, false, false);
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, 0, tc.timestep, nIter, tc.obstacles, false, false);
|
||||||
|
|
||||||
% Constraint adjacency matrix defined by LNA should be as follows
|
% Constraint adjacency matrix defined by LNA should be as follows
|
||||||
tc.assertEqual(tc.testClass.constraintAdjacencyMatrix, logical( ...
|
tc.assertEqual(tc.testClass.constraintAdjacencyMatrix, logical( ...
|
||||||
@@ -709,21 +715,22 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
% Initialize agent sensor model
|
% Initialize agent sensor model
|
||||||
sensor = sigmoidSensor;
|
sensor = sigmoidSensor;
|
||||||
alphaDist = l/2; % half of domain length/width
|
alphaDist = l/2; % half of domain length/width
|
||||||
sensor = sensor.initialize(alphaDist, 3, NaN, NaN, 15, 3);
|
sensor = sensor.initialize(alphaDist, 3, 15, 3);
|
||||||
|
|
||||||
% Initialize agents
|
% Initialize agents
|
||||||
|
nIter = 125;
|
||||||
commsRadius = d;
|
commsRadius = d;
|
||||||
tc.agents = {agent; agent; agent; agent; agent; agent; agent;};
|
tc.agents = {agent; agent; agent; agent; agent; agent; agent;};
|
||||||
tc.agents{1} = tc.agents{1}.initialize(tc.domain.center + [-0.9 * d/sqrt(2), 0.9 * d/sqrt(2), 0], zeros(1,3), 0, 0, geometry1, sensor, commsRadius);
|
tc.agents{1} = tc.agents{1}.initialize(tc.domain.center + [-0.9 * d/sqrt(2), 0.9 * d/sqrt(2), 0], geometry1, sensor, commsRadius, nIter, tc.initialStepSize);
|
||||||
tc.agents{2} = tc.agents{2}.initialize(tc.domain.center + [-0.5 * d, 0.25 * d, 0], zeros(1,3), 0, 0, geometry2, sensor, commsRadius);
|
tc.agents{2} = tc.agents{2}.initialize(tc.domain.center + [-0.5 * d, 0.25 * d, 0], geometry2, sensor, commsRadius, nIter, tc.initialStepSize);
|
||||||
tc.agents{3} = tc.agents{3}.initialize(tc.domain.center + [0.9 * d, 0, 0], zeros(1,3), 0, 0, geometry3, sensor, commsRadius);
|
tc.agents{3} = tc.agents{3}.initialize(tc.domain.center + [0.9 * d, 0, 0], geometry3, sensor, commsRadius, nIter, tc.initialStepSize);
|
||||||
tc.agents{4} = tc.agents{4}.initialize(tc.domain.center + [0.9 * d/sqrt(2), -0.9 * d/sqrt(2), 0], zeros(1,3), 0, 0, geometry4, sensor, commsRadius);
|
tc.agents{4} = tc.agents{4}.initialize(tc.domain.center + [0.9 * d/sqrt(2), -0.9 * d/sqrt(2), 0], geometry4, sensor, commsRadius, nIter, tc.initialStepSize);
|
||||||
tc.agents{5} = tc.agents{5}.initialize(tc.domain.center + [0, 0.9 * d, 0], zeros(1,3), 0, 0, geometry5, sensor, commsRadius);
|
tc.agents{5} = tc.agents{5}.initialize(tc.domain.center + [0, 0.9 * d, 0], geometry5, sensor, commsRadius, nIter, tc.initialStepSize);
|
||||||
tc.agents{6} = tc.agents{6}.initialize(tc.domain.center, zeros(1,3), 0, 0, geometry6, sensor, commsRadius);
|
tc.agents{6} = tc.agents{6}.initialize(tc.domain.center, geometry6, sensor, commsRadius, nIter, tc.initialStepSize);
|
||||||
tc.agents{7} = tc.agents{7}.initialize(tc.domain.center + [d/2, d/2, 0], zeros(1,3), 0, 0, geometry7, sensor, commsRadius);
|
tc.agents{7} = tc.agents{7}.initialize(tc.domain.center + [d/2, d/2, 0], geometry7, sensor, commsRadius, nIter, tc.initialStepSize);
|
||||||
|
|
||||||
% Initialize the simulation
|
% Initialize the simulation
|
||||||
tc.testClass = tc.testClass.initialize(tc.domain, tc.domain.objective, tc.agents, 0, tc.timestep, tc.partitoningFreq, 125, tc.obstacles, false, false);
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, 0, tc.timestep, nIter, tc.obstacles, false, false);
|
||||||
|
|
||||||
% Constraint adjacency matrix defined by LNA should be as follows
|
% Constraint adjacency matrix defined by LNA should be as follows
|
||||||
tc.assertEqual(tc.testClass.constraintAdjacencyMatrix, logical( ...
|
tc.assertEqual(tc.testClass.constraintAdjacencyMatrix, logical( ...
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ classdef test_sigmoidSensor < matlab.unittest.TestCase
|
|||||||
function tc = setup(tc)
|
function tc = setup(tc)
|
||||||
% Reinitialize sensor with random parameters
|
% Reinitialize sensor with random parameters
|
||||||
tc.testClass = sigmoidSensor;
|
tc.testClass = sigmoidSensor;
|
||||||
tc.testClass = tc.testClass.initialize(tc.alphaDistMin + rand * (tc.alphaDistMax - tc.alphaDistMin), tc.betaDistMin + rand * (tc.betaDistMax - tc.betaDistMin), NaN, NaN, tc.alphaTiltMin + rand * (tc.alphaTiltMax - tc.alphaTiltMin), tc.betaTiltMin + rand * (tc.betaTiltMax - tc.betaTiltMin));
|
tc.testClass = tc.testClass.initialize(tc.alphaDistMin + rand * (tc.alphaDistMax - tc.alphaDistMin), tc.betaDistMin + rand * (tc.betaDistMax - tc.betaDistMin), tc.alphaTiltMin + rand * (tc.alphaTiltMax - tc.alphaTiltMin), tc.betaTiltMin + rand * (tc.betaTiltMax - tc.betaTiltMin));
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -34,28 +34,28 @@ classdef test_sigmoidSensor < matlab.unittest.TestCase
|
|||||||
alphaTilt = 15; % degrees
|
alphaTilt = 15; % degrees
|
||||||
betaTilt = 3;
|
betaTilt = 3;
|
||||||
h = 1e-6;
|
h = 1e-6;
|
||||||
tc.testClass = tc.testClass.initialize(alphaDist, betaDist, NaN, NaN, alphaTilt, betaTilt);
|
tc.testClass = tc.testClass.initialize(alphaDist, betaDist, alphaTilt, betaTilt);
|
||||||
|
|
||||||
% Plot (optional)
|
% Plot (optional)
|
||||||
% tc.testClass.plotParameters();
|
% tc.testClass.plotParameters();
|
||||||
|
|
||||||
% Anticipate perfect performance for a point directly below and
|
% Anticipate perfect performance for a point directly below and
|
||||||
% extremely close
|
% extremely close
|
||||||
tc.verifyEqual(tc.testClass.sensorPerformance([0, 0, h], NaN, 0, [0, 0, 0]), 1, 'RelTol', 1e-3);
|
tc.verifyEqual(tc.testClass.sensorPerformance([0, 0, h], 0, [0, 0, 0]), 1, 'RelTol', 1e-3);
|
||||||
% It looks like mu_t can max out at really low values like 0.37
|
% It looks like mu_t can max out at really low values like 0.37
|
||||||
% when alphaTilt and betaTilt are small, which seems wrong
|
% when alphaTilt and betaTilt are small, which seems wrong
|
||||||
|
|
||||||
% Performance at nadir point, distance alphaDist should be 1/2 exactly
|
% Performance at nadir point, distance alphaDist should be 1/2 exactly
|
||||||
tc.verifyEqual(tc.testClass.sensorPerformance([0, 0, alphaDist], NaN, 0, [0, 0, 0]), 1/2);
|
tc.verifyEqual(tc.testClass.sensorPerformance([0, 0, alphaDist], 0, [0, 0, 0]), 1/2);
|
||||||
|
|
||||||
% Performance at (almost) 0 distance, alphaTilt should be 1/2
|
% Performance at (almost) 0 distance, alphaTilt should be 1/2
|
||||||
tc.verifyEqual(tc.testClass.sensorPerformance([0, 0, h], NaN, 0, [tand(alphaTilt)*h, 0, 0]), 1/2, 'RelTol', 1e-3);
|
tc.verifyEqual(tc.testClass.sensorPerformance([0, 0, h], 0, [tand(alphaTilt)*h, 0, 0]), 1/2, 'RelTol', 1e-3);
|
||||||
|
|
||||||
% Performance at great distance should be 0
|
% Performance at great distance should be 0
|
||||||
tc.verifyEqual(tc.testClass.sensorPerformance([0, 0, 10], NaN, 0, [0, 0, 0]), 0, 'AbsTol', 1e-9);
|
tc.verifyEqual(tc.testClass.sensorPerformance([0, 0, 10], 0, [0, 0, 0]), 0, 'AbsTol', 1e-9);
|
||||||
|
|
||||||
% Performance at great tilt should be 0
|
% Performance at great tilt should be 0
|
||||||
tc.verifyEqual(tc.testClass.sensorPerformance([0, 0, h], NaN, 0, [5, 5, 0]), 0, 'AbsTol', 1e-9);
|
tc.verifyEqual(tc.testClass.sensorPerformance([0, 0, h], 0, [5, 5, 0]), 0, 'AbsTol', 1e-9);
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
13
util/objectiveFunctionWrapper.m
Normal file
13
util/objectiveFunctionWrapper.m
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
function f = objectiveFunctionWrapper(center)
|
||||||
|
% Convenience function to generate MVNPDFs at a point
|
||||||
|
% Makes it look a lot neater to instantiate and sum these to make
|
||||||
|
% composite objectives in particular
|
||||||
|
arguments (Input)
|
||||||
|
center (1, 2) double;
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
f (1, 1) {mustBeA(f, 'function_handle')};
|
||||||
|
end
|
||||||
|
|
||||||
|
f = @(x, y) mvnpdf([x(:), y(:)], center);
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user