Compare commits
68
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c47b7229ba | ||
|
|
02189baaab | ||
|
|
af6a0447a8 | ||
|
|
62e015da42 | ||
|
|
ddecf63d68 | ||
|
|
591430ad8a | ||
|
|
1e7540226e | ||
|
|
4fe897455d | ||
|
|
7d1154d028 | ||
|
|
16673a437e | ||
|
|
6403e7cbcc | ||
|
|
066acd0949 | ||
|
|
8dfa0c337a | ||
|
|
492c5c2140 | ||
|
|
06f6af1511 | ||
|
|
c59b96f547 | ||
|
|
4735c2b77b | ||
|
|
d6a9c4ac06 | ||
|
|
fa8da50db1 | ||
|
|
61cdb96102 | ||
|
|
1d11ac4e90 | ||
|
|
843e5ba574 | ||
|
|
50eaad9504 | ||
|
|
14e372ae55 | ||
|
|
8315b6c511 | ||
|
|
4fa942564a | ||
|
|
6632c9885d | ||
|
|
1fa76c7023 | ||
|
|
33036c95fd | ||
|
|
557d8fe63c | ||
|
|
2cd1bb8659 | ||
|
|
06882d2f30 | ||
|
|
95ea19e546 | ||
|
|
96c91c3988 | ||
|
|
d70781fadc | ||
|
|
a688e9c285 | ||
|
|
d30fd9ccaa | ||
|
|
bdd018e566 | ||
|
|
28a6bfe3de | ||
|
|
c92ef143d1 | ||
|
|
6d16dfe974 | ||
|
|
1e0db2a46c | ||
|
|
f9aa2eb9d4 | ||
|
|
f296fd2803 | ||
|
|
7c87458b66 | ||
|
|
4e0f213d0c | ||
|
|
f9f070e2d0 | ||
|
|
352d2ed1de | ||
|
|
59805dff72 | ||
|
|
a8380985e1 | ||
|
|
55b69d4e33 | ||
|
|
779d7d2cc6 | ||
|
|
58d009c8fc | ||
|
|
b62f0f6410 | ||
|
|
fe5f3bb2be | ||
|
|
35b15db5d3 | ||
|
|
f50e3e2832 | ||
|
|
e53b721f34 | ||
|
|
db20f11ea8 | ||
|
|
86342c4572 | ||
|
|
b9a2a83ac6 | ||
|
|
9dbd29849f | ||
|
|
12dbde7a02 | ||
|
|
c9ac9d7725 | ||
|
|
afa5d79c1d | ||
|
|
e0f365b21b | ||
|
|
4363914215 | ||
|
|
855b28b066 |
No files matched your search
@@ -45,3 +45,6 @@ sandbox/*
|
|||||||
# Videos
|
# Videos
|
||||||
*.mp4
|
*.mp4
|
||||||
*.avi
|
*.avi
|
||||||
|
|
||||||
|
# Figures
|
||||||
|
*.fig
|
||||||
+11
-6
@@ -1,16 +1,12 @@
|
|||||||
classdef agent
|
classdef agent
|
||||||
properties (SetAccess = private, GetAccess = public)
|
properties (SetAccess = public, GetAccess = public)
|
||||||
% Identifiers
|
% Identifiers
|
||||||
index = NaN;
|
|
||||||
label = "";
|
label = "";
|
||||||
|
|
||||||
% Sensor
|
% Sensor
|
||||||
sensorModel;
|
sensorModel;
|
||||||
sensingLength = 0.05; % length parameter used by sensing function
|
sensingLength = 0.05; % length parameter used by sensing function
|
||||||
|
|
||||||
% Guidance
|
|
||||||
guidanceModel;
|
|
||||||
|
|
||||||
% 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
|
||||||
@@ -20,20 +16,29 @@ classdef agent
|
|||||||
|
|
||||||
% Collision
|
% Collision
|
||||||
collisionGeometry;
|
collisionGeometry;
|
||||||
|
barrierFunction;
|
||||||
|
dBarrierFunction;
|
||||||
|
|
||||||
% FOV cone
|
% FOV cone
|
||||||
fovGeometry;
|
fovGeometry;
|
||||||
|
|
||||||
% Communication
|
% Communication
|
||||||
comRange = NaN;
|
comRange = NaN;
|
||||||
|
commsGeometry = spherical;
|
||||||
|
lesserNeighbors = [];
|
||||||
|
|
||||||
|
performance = 0;
|
||||||
|
|
||||||
% Plotting
|
% Plotting
|
||||||
scatterPoints;
|
scatterPoints;
|
||||||
|
debug = false;
|
||||||
|
debugFig;
|
||||||
|
plotCommsGeometry = true;
|
||||||
end
|
end
|
||||||
|
|
||||||
methods (Access = public)
|
methods (Access = public)
|
||||||
[obj] = initialize(obj, pos, vel, pan, tilt, collisionGeometry, sensorModel, guidanceModel, comRange, index, label);
|
[obj] = initialize(obj, pos, vel, pan, tilt, collisionGeometry, sensorModel, guidanceModel, comRange, index, label);
|
||||||
[obj] = run(obj, sensingObjective, domain, partitioning);
|
[obj] = run(obj, domain, partitioning, t, index);
|
||||||
[obj, f] = plot(obj, ind, f);
|
[obj, f] = plot(obj, ind, f);
|
||||||
updatePlots(obj);
|
updatePlots(obj);
|
||||||
end
|
end
|
||||||
|
|||||||
+56
-9
@@ -1,4 +1,4 @@
|
|||||||
function obj = initialize(obj, pos, vel, pan, tilt, collisionGeometry, sensorModel, guidanceModel, comRange, index, label)
|
function obj = initialize(obj, pos, vel, pan, tilt, collisionGeometry, sensorModel, comRange, label, debug, 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;
|
||||||
@@ -6,11 +6,11 @@ function obj = initialize(obj, pos, vel, pan, tilt, collisionGeometry, sensorMod
|
|||||||
pan (1, 1) double;
|
pan (1, 1) double;
|
||||||
tilt (1, 1) double;
|
tilt (1, 1) double;
|
||||||
collisionGeometry (1, 1) {mustBeGeometry};
|
collisionGeometry (1, 1) {mustBeGeometry};
|
||||||
sensorModel (1, 1) {mustBeSensor}
|
sensorModel (1, 1) {mustBeSensor};
|
||||||
guidanceModel (1, 1) {mustBeA(guidanceModel, 'function_handle')};
|
comRange (1, 1) double;
|
||||||
comRange (1, 1) double = NaN;
|
|
||||||
index (1, 1) double = NaN;
|
|
||||||
label (1, 1) string = "";
|
label (1, 1) string = "";
|
||||||
|
debug (1, 1) logical = false;
|
||||||
|
plotCommsGeometry (1, 1) logical = false;
|
||||||
end
|
end
|
||||||
arguments (Output)
|
arguments (Output)
|
||||||
obj (1, 1) {mustBeA(obj, 'agent')};
|
obj (1, 1) {mustBeA(obj, 'agent')};
|
||||||
@@ -22,12 +22,59 @@ function obj = initialize(obj, pos, vel, pan, tilt, collisionGeometry, sensorMod
|
|||||||
obj.tilt = tilt;
|
obj.tilt = tilt;
|
||||||
obj.collisionGeometry = collisionGeometry;
|
obj.collisionGeometry = collisionGeometry;
|
||||||
obj.sensorModel = sensorModel;
|
obj.sensorModel = sensorModel;
|
||||||
obj.guidanceModel = guidanceModel;
|
|
||||||
obj.comRange = comRange;
|
|
||||||
obj.index = index;
|
|
||||||
obj.label = label;
|
obj.label = label;
|
||||||
|
obj.debug = debug;
|
||||||
|
obj.plotCommsGeometry = plotCommsGeometry;
|
||||||
|
|
||||||
|
% Add spherical geometry based on com range
|
||||||
|
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], tan(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:2), 0], tand(obj.sensorModel.alphaTilt) * obj.pos(3), obj.pos(3), REGION_TYPE.FOV, sprintf("%s FOV", obj.label));
|
||||||
end
|
end
|
||||||
@@ -30,6 +30,11 @@ function [obj, f] = plot(obj, ind, f)
|
|||||||
% Plot collision geometry
|
% Plot collision geometry
|
||||||
[obj.collisionGeometry, f] = obj.collisionGeometry.plotWireframe(ind, f);
|
[obj.collisionGeometry, f] = obj.collisionGeometry.plotWireframe(ind, f);
|
||||||
|
|
||||||
|
% Plot communications geometry
|
||||||
|
if obj.plotCommsGeometry
|
||||||
|
[obj.commsGeometry, f] = obj.commsGeometry.plotWireframe(ind, f);
|
||||||
|
end
|
||||||
|
|
||||||
% Plot FOV geometry
|
% Plot FOV geometry
|
||||||
[obj.fovGeometry, f] = obj.fovGeometry.plot(ind, f);
|
[obj.fovGeometry, f] = obj.fovGeometry.plot(ind, f);
|
||||||
end
|
end
|
||||||
+138
-11
@@ -1,28 +1,155 @@
|
|||||||
function obj = run(obj, sensingObjective, domain, partitioning)
|
function obj = run(obj, domain, partitioning, t, index)
|
||||||
arguments (Input)
|
arguments (Input)
|
||||||
obj (1, 1) {mustBeA(obj, 'agent')};
|
obj (1, 1) {mustBeA(obj, 'agent')};
|
||||||
sensingObjective (1, 1) {mustBeA(sensingObjective, 'sensingObjective')};
|
|
||||||
domain (1, 1) {mustBeGeometry};
|
domain (1, 1) {mustBeGeometry};
|
||||||
partitioning (:, :) double;
|
partitioning (:, :) double;
|
||||||
|
t (1, 1) double;
|
||||||
|
index (1, 1) double;
|
||||||
end
|
end
|
||||||
arguments (Output)
|
arguments (Output)
|
||||||
obj (1, 1) {mustBeA(obj, 'agent')};
|
obj (1, 1) {mustBeA(obj, 'agent')};
|
||||||
end
|
end
|
||||||
|
|
||||||
% Do sensing
|
% Collect objective function values across partition
|
||||||
[sensedValues, sensedPositions] = obj.sensorModel.sense(obj, sensingObjective, domain, partitioning);
|
partitionMask = partitioning == index;
|
||||||
|
objectiveValues = domain.objective.values(partitionMask); % f(omega) on W_n
|
||||||
|
|
||||||
% Determine next planned position
|
% Compute sensor performance across partition
|
||||||
nextPos = obj.guidanceModel(sensedValues, sensedPositions, obj.pos);
|
maskedX = domain.objective.X(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
|
||||||
|
F = NaN(size(partitionMask));
|
||||||
|
F(partitionMask) = objectiveValues;
|
||||||
|
S = NaN(size(partitionMask));
|
||||||
|
Slower = S;
|
||||||
|
Shigher = S;
|
||||||
|
S(partitionMask) = sensorValues;
|
||||||
|
Slower(partitionMask) = sensorValuesLower;
|
||||||
|
Shigher(partitionMask) = sensorValuesHigher;
|
||||||
|
|
||||||
|
% Find agent's performance
|
||||||
|
C = S .* F;
|
||||||
|
obj.performance = [obj.performance, sum(C(~isnan(C)))]; % at current Z only
|
||||||
|
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
|
||||||
|
|
||||||
|
% return now if there is no data to work with, and do not move
|
||||||
|
if all(isnan(nGradC), 'all')
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
% Use largest grad(C) value to find the direction of the next position
|
||||||
|
[xNextIdx, yNextIdx, zNextIdx] = ind2sub(size(nGradC), find(nGradC == max(nGradC, [], 'all')));
|
||||||
|
% switch them
|
||||||
|
temp = xNextIdx;
|
||||||
|
xNextIdx = yNextIdx;
|
||||||
|
yNextIdx = temp;
|
||||||
|
|
||||||
|
roundingScale = 10^-log10(domain.objective.discretizationStep);
|
||||||
|
zKey = zFactor * [1; 0; -1];
|
||||||
|
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
|
||||||
|
|
||||||
|
% Determine next position
|
||||||
|
vDir = (pNext - obj.pos)./norm(pNext - obj.pos, 2);
|
||||||
|
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
|
||||||
% (dynamics not modeled at this time)
|
|
||||||
obj.lastPos = obj.pos;
|
obj.lastPos = obj.pos;
|
||||||
obj.pos = nextPos;
|
obj.pos = nextPos;
|
||||||
|
|
||||||
% Calculate movement
|
|
||||||
d = obj.pos - obj.collisionGeometry.center;
|
|
||||||
|
|
||||||
% Reinitialize collision geometry in the new position
|
% Reinitialize collision geometry in the new position
|
||||||
obj.collisionGeometry = obj.collisionGeometry.initialize([obj.collisionGeometry.minCorner; obj.collisionGeometry.maxCorner] + d, obj.collisionGeometry.tag, obj.collisionGeometry.label);
|
d = obj.pos - obj.collisionGeometry.center;
|
||||||
|
if isa(obj.collisionGeometry, 'rectangularPrism')
|
||||||
|
obj.collisionGeometry = obj.collisionGeometry.initialize([obj.collisionGeometry.minCorner; obj.collisionGeometry.maxCorner] + d, obj.collisionGeometry.tag, obj.collisionGeometry.label);
|
||||||
|
elseif isa(obj.collisionGeometry, 'spherical')
|
||||||
|
obj.collisionGeometry = obj.collisionGeometry.initialize(obj.collisionGeometry.center + d, obj.collisionGeometry.radius, obj.collisionGeometry.tag, obj.collisionGeometry.label);
|
||||||
|
else
|
||||||
|
error("?");
|
||||||
|
end
|
||||||
end
|
end
|
||||||
@@ -25,6 +25,17 @@ function updatePlots(obj)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
% Communications geometry edges
|
||||||
|
if obj.plotCommsGeometry
|
||||||
|
for jj = 1:size(obj.commsGeometry.lines, 2)
|
||||||
|
for ii = 1:size(obj.collisionGeometry.lines(:, jj), 1)
|
||||||
|
obj.collisionGeometry.lines(ii, jj).XData = obj.collisionGeometry.lines(ii, jj).XData + deltaPos(1);
|
||||||
|
obj.collisionGeometry.lines(ii, jj).YData = obj.collisionGeometry.lines(ii, jj).YData + deltaPos(2);
|
||||||
|
obj.collisionGeometry.lines(ii, jj).ZData = obj.collisionGeometry.lines(ii, jj).ZData + deltaPos(3);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
% 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
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
function [obj] = constrainMotion(obj)
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'miSim')};
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'miSim')};
|
||||||
|
end
|
||||||
|
|
||||||
|
if size(obj.agents, 1) < 2
|
||||||
|
nAAPairs = 0;
|
||||||
|
else
|
||||||
|
nAAPairs = nchoosek(size(obj.agents, 1), 2); % unique agent/agent pairs
|
||||||
|
end
|
||||||
|
|
||||||
|
agents = [obj.agents{:}];
|
||||||
|
v = reshape(([agents.pos] - [agents.lastPos])./obj.timestep, 3, size(obj.agents, 1))';
|
||||||
|
|
||||||
|
% Initialize QP based on number of agents and obstacles
|
||||||
|
nAOPairs = size(obj.agents, 1) * size(obj.obstacles, 1); % unique agent/obstacle pairs
|
||||||
|
nADPairs = size(obj.agents, 1) * 5; % agents x (4 walls + 1 ceiling)
|
||||||
|
nLNAPairs = sum(obj.constraintAdjacencyMatrix, 'all') - size(obj.agents, 1);
|
||||||
|
total = nAAPairs + nAOPairs + nADPairs + nLNAPairs;
|
||||||
|
kk = 1;
|
||||||
|
A = zeros(total, 3 * size(obj.agents, 1));
|
||||||
|
b = zeros(total, 1);
|
||||||
|
|
||||||
|
% Set up collision avoidance constraints
|
||||||
|
h = NaN(size(obj.agents, 1));
|
||||||
|
h(logical(eye(size(obj.agents, 1)))) = 0; % self value is 0
|
||||||
|
for ii = 1:(size(obj.agents, 1) - 1)
|
||||||
|
for jj = (ii + 1):size(obj.agents, 1)
|
||||||
|
h(ii, jj) = norm(agents(ii).pos - agents(jj).pos)^2 - (agents(ii).collisionGeometry.radius + agents(jj).collisionGeometry.radius)^2;
|
||||||
|
h(jj, ii) = h(ii, jj);
|
||||||
|
|
||||||
|
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));
|
||||||
|
b(kk) = obj.barrierGain * h(ii, jj)^3;
|
||||||
|
kk = kk + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
hObs = NaN(size(obj.agents, 1), size(obj.obstacles, 1));
|
||||||
|
% Set up obstacle avoidance constraints
|
||||||
|
for ii = 1:size(obj.agents, 1)
|
||||||
|
for jj = 1:size(obj.obstacles, 1)
|
||||||
|
% find closest position to agent on/in obstacle
|
||||||
|
cPos = obj.obstacles{jj}.closestToPoint(agents(ii).pos);
|
||||||
|
|
||||||
|
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);
|
||||||
|
b(kk) = obj.barrierGain * hObs(ii, jj)^3;
|
||||||
|
|
||||||
|
kk = kk + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
% Set up domain constraints (walls and ceiling only)
|
||||||
|
% Floor constraint is implicit with an obstacle corresponding to the
|
||||||
|
% minimum allowed altitude, but I included it anyways
|
||||||
|
for ii = 1:size(obj.agents, 1)
|
||||||
|
% X minimum
|
||||||
|
h_xMin = (agents(ii).pos(1) - obj.domain.minCorner(1)) - agents(ii).collisionGeometry.radius;
|
||||||
|
A(kk, (3 * ii - 2):(3 * ii)) = [-1, 0, 0];
|
||||||
|
b(kk) = obj.barrierGain * h_xMin^3;
|
||||||
|
kk = kk + 1;
|
||||||
|
|
||||||
|
% X maximum
|
||||||
|
h_xMax = (obj.domain.maxCorner(1) - agents(ii).pos(1)) - agents(ii).collisionGeometry.radius;
|
||||||
|
A(kk, (3 * ii - 2):(3 * ii)) = [1, 0, 0];
|
||||||
|
b(kk) = obj.barrierGain * h_xMax^3;
|
||||||
|
kk = kk + 1;
|
||||||
|
|
||||||
|
% Y minimum
|
||||||
|
h_yMin = (agents(ii).pos(2) - obj.domain.minCorner(2)) - agents(ii).collisionGeometry.radius;
|
||||||
|
A(kk, (3 * ii - 2):(3 * ii)) = [0, -1, 0];
|
||||||
|
b(kk) = obj.barrierGain * h_yMin^3;
|
||||||
|
kk = kk + 1;
|
||||||
|
|
||||||
|
% Y maximum
|
||||||
|
h_yMax = (obj.domain.maxCorner(2) - agents(ii).pos(2)) - agents(ii).collisionGeometry.radius;
|
||||||
|
A(kk, (3 * ii - 2):(3 * ii)) = [0, 1, 0];
|
||||||
|
b(kk) = obj.barrierGain * h_yMax^3;
|
||||||
|
kk = kk + 1;
|
||||||
|
|
||||||
|
% Z minimum
|
||||||
|
h_zMin = (agents(ii).pos(3) - obj.domain.minCorner(3)) - agents(ii).collisionGeometry.radius;
|
||||||
|
A(kk, (3 * ii - 2):(3 * ii)) = [0, 0, -1];
|
||||||
|
b(kk) = obj.barrierGain * h_zMin^3;
|
||||||
|
kk = kk + 1;
|
||||||
|
|
||||||
|
% Z maximum
|
||||||
|
h_zMax = (obj.domain.maxCorner(2) - agents(ii).pos(2)) - agents(ii).collisionGeometry.radius;
|
||||||
|
A(kk, (3 * ii - 2):(3 * ii)) = [0, 0, 1];
|
||||||
|
b(kk) = obj.barrierGain * h_zMax^3;
|
||||||
|
kk = kk + 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
% Save off h function values (ignoring network constraints which may evolve in time)
|
||||||
|
obj.h(:, obj.timestepIndex) = [h(triu(true(size(obj.agents, 1)), 1)); reshape(hObs, [], 1); h_xMin; h_xMax; h_yMin; h_yMax; h_zMin; h_zMax;];
|
||||||
|
|
||||||
|
% Add communication network constraints
|
||||||
|
hComms = NaN(size(obj.agents, 1));
|
||||||
|
hComms(logical(eye(size(obj.agents, 1)))) = 0;
|
||||||
|
for ii = 1:(size(obj.agents, 1) - 1)
|
||||||
|
for jj = (ii + 1):size(obj.agents, 1)
|
||||||
|
if obj.constraintAdjacencyMatrix(ii, jj)
|
||||||
|
hComms(ii, jj) = min([obj.agents{ii}.commsGeometry.radius, obj.agents{jj}.commsGeometry.radius])^2 - norm(agents(ii).pos - agents(jj).pos)^2;
|
||||||
|
|
||||||
|
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));
|
||||||
|
b(kk) = obj.barrierGain * hComms(ii, jj);
|
||||||
|
|
||||||
|
% 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;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
% Solve QP program generated earlier
|
||||||
|
vhat = reshape(v', 3 * size(obj.agents, 1), 1);
|
||||||
|
H = 2 * eye(3 * size(obj.agents, 1));
|
||||||
|
f = -2 * vhat;
|
||||||
|
|
||||||
|
% Update solution based on constraints
|
||||||
|
assert(size(A,2) == size(H,1))
|
||||||
|
assert(size(A,1) == size(b,1))
|
||||||
|
assert(size(H,1) == length(f))
|
||||||
|
opt = optimoptions('quadprog', 'Display', 'off');
|
||||||
|
[vNew, ~, exitflag, m] = quadprog(sparse(H), double(f), A, b, [],[], [], [], [], opt);
|
||||||
|
assert(exitflag == 1, sprintf('quadprog failure... %s%s', newline, m.message));
|
||||||
|
vNew = reshape(vNew, 3, size(obj.agents, 1))';
|
||||||
|
|
||||||
|
if exitflag <= 0
|
||||||
|
warning("QP failed, continuing with unconstrained solution...")
|
||||||
|
vNew = v;
|
||||||
|
end
|
||||||
|
|
||||||
|
% Update the "next position" that was previously set by unconstrained
|
||||||
|
% GA using the constrained solution produced here
|
||||||
|
for ii = 1:size(vNew, 1)
|
||||||
|
obj.agents{ii}.pos = obj.agents{ii}.lastPos + vNew(ii, :) * obj.timestep;
|
||||||
|
end
|
||||||
|
|
||||||
|
% Here we run this at the simulation level, but in reality there is no
|
||||||
|
% parent level, so this would be run independently on each agent.
|
||||||
|
% Running at the simulation level is just meant to simplify the
|
||||||
|
% simulation
|
||||||
|
|
||||||
|
end
|
||||||
+47
-3
@@ -1,20 +1,34 @@
|
|||||||
function obj = initialize(obj, domain, objective, agents, timestep, partitoningFreq, maxIter, obstacles)
|
function obj = initialize(obj, domain, objective, agents, minAlt, timestep, partitoningFreq, 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')};
|
objective (1, 1) {mustBeA(objective, 'sensingObjective')};
|
||||||
agents (:, 1) cell;
|
agents (:, 1) cell;
|
||||||
|
minAlt (1, 1) double = 1;
|
||||||
timestep (:, 1) double = 0.05;
|
timestep (:, 1) double = 0.05;
|
||||||
partitoningFreq (:, 1) double = 0.25
|
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;
|
||||||
|
makeVideo (1, 1) logical = true;
|
||||||
end
|
end
|
||||||
arguments (Output)
|
arguments (Output)
|
||||||
obj (1, 1) {mustBeA(obj, 'miSim')};
|
obj (1, 1) {mustBeA(obj, 'miSim')};
|
||||||
end
|
end
|
||||||
|
|
||||||
|
% enable/disable plotting and video writer
|
||||||
|
obj.makePlots = makePlots;
|
||||||
|
if ~obj.makePlots
|
||||||
|
if makeVideo
|
||||||
|
warning("makeVideo set to true, but makePlots set to false. Setting makeVideo to false.");
|
||||||
|
makeVideo = false;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
obj.makeVideo = makeVideo;
|
||||||
|
|
||||||
% Define simulation time parameters
|
% Define simulation time parameters
|
||||||
obj.timestep = timestep;
|
obj.timestep = timestep;
|
||||||
|
obj.timestepIndex = 0;
|
||||||
obj.maxIter = maxIter - 1;
|
obj.maxIter = maxIter - 1;
|
||||||
|
|
||||||
% Define domain
|
% Define domain
|
||||||
@@ -24,26 +38,56 @@ function obj = initialize(obj, domain, objective, agents, timestep, partitoningF
|
|||||||
% 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
|
||||||
|
% represent the minimum allowable altitude
|
||||||
|
obj.minAlt = minAlt;
|
||||||
|
if obj.minAlt > 0
|
||||||
|
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");
|
||||||
|
end
|
||||||
|
|
||||||
% Define objective
|
% Define objective
|
||||||
obj.objective = objective;
|
obj.objective = objective;
|
||||||
|
|
||||||
% Define agents
|
% Define agents
|
||||||
obj.agents = agents;
|
obj.agents = agents;
|
||||||
|
obj.constraintAdjacencyMatrix = logical(eye(size(agents, 1)));
|
||||||
|
|
||||||
% Compute adjacency matrix
|
% Set labels for agents and collision geometries in cases where they
|
||||||
|
% were not provieded at the time of their initialization
|
||||||
|
for ii = 1:size(obj.agents, 1)
|
||||||
|
% Agent
|
||||||
|
if isempty(char(obj.agents{ii}.label))
|
||||||
|
obj.agents{ii}.label = sprintf("Agent %d", ii);
|
||||||
|
end
|
||||||
|
|
||||||
|
% Collision geometry
|
||||||
|
if isempty(char(obj.agents{ii}.collisionGeometry.label))
|
||||||
|
obj.agents{ii}.collisionGeometry.label = sprintf("Agent %d Collision Geometry", ii);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
% Compute adjacency matrix and lesser neighbors
|
||||||
obj = obj.updateAdjacency();
|
obj = obj.updateAdjacency();
|
||||||
|
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));
|
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.fPerf = figure;
|
|
||||||
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
|
||||||
|
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);
|
||||||
|
|
||||||
% Create initial partitioning
|
% Create initial partitioning
|
||||||
obj = obj.partition();
|
obj = obj.partition();
|
||||||
|
|
||||||
|
% Initialize variable that will store agent positions for trail plots
|
||||||
|
obj.posHist = NaN(size(obj.agents, 1), obj.maxIter + 1, 3);
|
||||||
|
obj.posHist(1:size(obj.agents, 1), 1, 1:3) = reshape(cell2mat(cellfun(@(x) x.pos, obj.agents, 'UniformOutput', false)), size(obj.agents, 1), 1, 3);
|
||||||
|
|
||||||
% Set up plots showing initialized state
|
% Set up plots showing initialized state
|
||||||
obj = obj.plot();
|
obj = obj.plot();
|
||||||
end
|
end
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
function obj = lesserNeighbor(obj)
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'miSim')};
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'miSim')};
|
||||||
|
end
|
||||||
|
|
||||||
|
% initialize solution with self-connections only
|
||||||
|
constraintAdjacencyMatrix = logical(eye(size(obj.agents, 1)));
|
||||||
|
|
||||||
|
for ii = 1:size(obj.agents, 1)
|
||||||
|
% Find lesser neighbors of each agent
|
||||||
|
% Lesser neighbors of ii are jj < ii in range of ii
|
||||||
|
lesserNeighbors = [];
|
||||||
|
for jj = 1:(ii - 1)
|
||||||
|
if obj.adjacency(ii, jj)
|
||||||
|
lesserNeighbors = [lesserNeighbors, jj];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
obj.agents{ii}.lesserNeighbors = lesserNeighbors;
|
||||||
|
|
||||||
|
% Early exit for isolated agents
|
||||||
|
if isempty(obj.agents{ii}.lesserNeighbors)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
% Focus on subgraph defined by lesser neighbors
|
||||||
|
subgraphAdjacency = obj.adjacency(obj.agents{ii}.lesserNeighbors, obj.agents{ii}.lesserNeighbors);
|
||||||
|
|
||||||
|
% Find connected components in each agent's subgraph
|
||||||
|
% TODO: rewrite this using matlab "conncomp" function?
|
||||||
|
visited = false(size(subgraphAdjacency, 1), 1);
|
||||||
|
components = {};
|
||||||
|
for jj = 1:size(subgraphAdjacency, 1)
|
||||||
|
if ~visited(jj)
|
||||||
|
reachable = bfs(subgraphAdjacency, jj);
|
||||||
|
visited(reachable) = true;
|
||||||
|
components{end+1} = obj.agents{ii}.lesserNeighbors(reachable);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
% Connect to the greatest index in each connected component in the
|
||||||
|
% lesser neighborhood of this agent
|
||||||
|
for jj = 1:size(components, 2)
|
||||||
|
constraintAdjacencyMatrix(ii, max(components{jj})) = true;
|
||||||
|
constraintAdjacencyMatrix(max(components{jj}), ii) = true;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
obj.constraintAdjacencyMatrix = constraintAdjacencyMatrix | constraintAdjacencyMatrix';
|
||||||
|
end
|
||||||
|
|
||||||
|
function cComp = bfs(subgraphAdjacency, startIdx)
|
||||||
|
n = size(subgraphAdjacency, 1);
|
||||||
|
visited = false(1, n);
|
||||||
|
queue = startIdx;
|
||||||
|
cComp = startIdx;
|
||||||
|
visited(startIdx) = true;
|
||||||
|
|
||||||
|
while ~isempty(queue)
|
||||||
|
current = queue(1);
|
||||||
|
queue(1) = [];
|
||||||
|
|
||||||
|
% Find all neighbors of current node in the subgraph
|
||||||
|
neighbors = find(subgraphAdjacency(current, :));
|
||||||
|
|
||||||
|
for neighbor = neighbors
|
||||||
|
if ~visited(neighbor)
|
||||||
|
visited(neighbor) = true;
|
||||||
|
cComp = [cComp, neighbor];
|
||||||
|
queue = [queue, neighbor];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
cComp = sort(cComp);
|
||||||
|
end
|
||||||
+27
-6
@@ -4,6 +4,7 @@ classdef miSim
|
|||||||
% Simulation parameters
|
% Simulation parameters
|
||||||
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)
|
||||||
partitioningFreq = NaN; % number of simulation timesteps at which the partitioning routine is re-run
|
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;
|
||||||
@@ -11,46 +12,66 @@ classdef miSim
|
|||||||
obstacles = cell(0, 1); % geometries that define obstacles within the domain
|
obstacles = cell(0, 1); % geometries that define obstacles within the domain
|
||||||
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
|
||||||
sensorPerformanceMinimum = 1e-6; % minimum sensor performance to allow assignment of a point in the domain to a partition
|
sensorPerformanceMinimum = 1e-6; % minimum sensor performance to allow assignment of a point in the domain to a partition
|
||||||
partitioning = NaN;
|
partitioning = NaN;
|
||||||
performance = NaN; % current cumulative sensor performance
|
perf; % sensor performance timeseries array
|
||||||
|
performance = 0; % simulation performance timeseries vector
|
||||||
|
barrierGain = 100; % collision avoidance parameter
|
||||||
|
minAlt = 1; % minimum allowed altitude constraint
|
||||||
|
|
||||||
|
fPerf; % performance plot figure
|
||||||
end
|
end
|
||||||
|
|
||||||
properties (Access = private)
|
properties (Access = private)
|
||||||
% Sim
|
% Sim
|
||||||
t = NaN; % current sim time
|
t = NaN; % current sim time
|
||||||
perf; % sensor performance timeseries array
|
|
||||||
times;
|
times;
|
||||||
partitioningTimes;
|
partitioningTimes;
|
||||||
|
|
||||||
% Plot objects
|
% Plot objects
|
||||||
f = firstPlotSetup(); % main plotting tiled layout figure
|
makePlots = true; % enable/disable simulation plotting (performance implications)
|
||||||
|
makeVideo = true; % enable/disable VideoWriter (performance implications)
|
||||||
|
f; % main plotting tiled layout figure
|
||||||
connectionsPlot; % objects for lines connecting agents in spatial plots
|
connectionsPlot; % objects for lines connecting agents in spatial plots
|
||||||
graphPlot; % objects for abstract network graph plot
|
graphPlot; % objects for abstract network graph plot
|
||||||
partitionPlot; % objects for partition plot
|
partitionPlot; % objects for partition plot
|
||||||
|
|
||||||
fPerf; % performance plot figure
|
|
||||||
performancePlot; % objects for sensor performance plot
|
performancePlot; % objects for sensor performance plot
|
||||||
|
|
||||||
|
posHist; % data for trail plot
|
||||||
|
trailPlot; % objects for agent trail plot
|
||||||
|
|
||||||
% Indicies for various plot types in the main tiled layout figure
|
% Indicies for various plot types in the main tiled layout figure
|
||||||
spatialPlotIndices = [6, 4, 3, 2];
|
spatialPlotIndices = [6, 4, 3, 2];
|
||||||
objectivePlotIndices = [6, 4];
|
objectivePlotIndices = [6, 4];
|
||||||
networkGraphIndex = 5;
|
networkGraphIndex = 5;
|
||||||
partitionGraphIndex = 1;
|
partitionGraphIndex = 1;
|
||||||
|
|
||||||
|
% CBF plotting
|
||||||
|
h; % h function values
|
||||||
|
hf; % h function plotting figure
|
||||||
|
caPlot; % objects for collision avoidance h function plot
|
||||||
|
obsPlot; % objects for obstacle h function plot
|
||||||
|
domPlot; % objects for domain h function plot
|
||||||
end
|
end
|
||||||
|
|
||||||
methods (Access = public)
|
methods (Access = public)
|
||||||
[obj] = initialize(obj, domain, objective, agents, timestep, partitoningFreq, maxIter, obstacles);
|
[obj] = initialize(obj, domain, objective, agents, timestep, partitoningFreq, maxIter, obstacles);
|
||||||
[obj] = run(obj);
|
[obj] = run(obj);
|
||||||
|
[obj] = lesserNeighbor(obj);
|
||||||
|
[obj] = constrainMotion(obj);
|
||||||
[obj] = partition(obj);
|
[obj] = partition(obj);
|
||||||
[obj] = updateAdjacency(obj);
|
[obj] = updateAdjacency(obj);
|
||||||
[obj] = plot(obj);
|
[obj] = plot(obj);
|
||||||
[obj] = plotConnections(obj);
|
[obj] = plotConnections(obj);
|
||||||
[obj] = plotPartitions(obj);
|
[obj] = plotPartitions(obj);
|
||||||
[obj] = plotGraph(obj);
|
[obj] = plotGraph(obj);
|
||||||
|
[obj] = plotTrails(obj);
|
||||||
|
[obj] = plotH(obj);
|
||||||
[obj] = updatePlots(obj, updatePartitions);
|
[obj] = updatePlots(obj, updatePartitions);
|
||||||
|
validate(obj);
|
||||||
end
|
end
|
||||||
methods (Access = private)
|
methods (Access = private)
|
||||||
[v] = setupVideoWriter(obj);
|
[v] = setupVideoWriter(obj);
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
+10
-13
@@ -16,21 +16,18 @@ function obj = partition(obj)
|
|||||||
[~, idx] = max(agentPerformances, [], 3);
|
[~, idx] = max(agentPerformances, [], 3);
|
||||||
|
|
||||||
% Collect agent indices in the same way as performance
|
% Collect agent indices in the same way as performance
|
||||||
agentInds = cellfun(@(x) x.index * ones(size(obj.objective.X)), obj.agents, 'UniformOutput', false);
|
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{end + 1} = zeros(size(agentInds{end})); % index for no assignment
|
||||||
agentInds = cat(3, agentInds{:});
|
agentInds = cat(3, agentInds{:});
|
||||||
|
|
||||||
% Get highest performing agent's index
|
% Use highest performing agent's index to form partitions
|
||||||
[m,n,~] = size(agentInds);
|
[m, n, ~] = size(agentInds);
|
||||||
[jj,kk] = ndgrid(1:m, 1:n);
|
[jj, kk] = ndgrid(1:m, 1:n);
|
||||||
obj.partitioning = agentInds(sub2ind(size(agentInds), jj, kk, idx));
|
obj.partitioning = agentInds(sub2ind(size(agentInds), jj, kk, idx));
|
||||||
|
|
||||||
% Get individual agent sensor performance
|
|
||||||
nowIdx = [0; obj.partitioningTimes] == obj.t;
|
|
||||||
for ii = 1:size(obj.agents, 1)
|
|
||||||
obj.perf(ii, nowIdx) = sum(agentPerformances(sub2ind(size(agentInds), jj, kk, ii)), 'all');
|
|
||||||
end
|
|
||||||
|
|
||||||
% Current total performance
|
|
||||||
obj.perf(end, nowIdx) = sum(obj.perf(1:(end - 1), nowIdx));
|
|
||||||
end
|
end
|
||||||
+12
-1
@@ -5,6 +5,11 @@ function obj = plot(obj)
|
|||||||
arguments (Output)
|
arguments (Output)
|
||||||
obj (1, 1) {mustBeA(obj, 'miSim')};
|
obj (1, 1) {mustBeA(obj, 'miSim')};
|
||||||
end
|
end
|
||||||
|
|
||||||
|
% fast exit when plotting is disabled
|
||||||
|
if ~obj.makePlots
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
% Plot domain
|
% Plot domain
|
||||||
[obj.domain, obj.f] = obj.domain.plotWireframe(obj.spatialPlotIndices);
|
[obj.domain, obj.f] = obj.domain.plotWireframe(obj.spatialPlotIndices);
|
||||||
@@ -17,7 +22,7 @@ function obj = plot(obj)
|
|||||||
% Plot objective gradient
|
% Plot objective gradient
|
||||||
obj.f = obj.domain.objective.plot(obj.objectivePlotIndices, obj.f);
|
obj.f = obj.domain.objective.plot(obj.objectivePlotIndices, obj.f);
|
||||||
|
|
||||||
% Plot agents and their collision geometries
|
% Plot agents and their collision/communications geometries
|
||||||
for ii = 1:size(obj.agents, 1)
|
for ii = 1:size(obj.agents, 1)
|
||||||
[obj.agents{ii}, obj.f] = obj.agents{ii}.plot(obj.spatialPlotIndices, obj.f);
|
[obj.agents{ii}, obj.f] = obj.agents{ii}.plot(obj.spatialPlotIndices, obj.f);
|
||||||
end
|
end
|
||||||
@@ -31,6 +36,9 @@ function obj = plot(obj)
|
|||||||
% Plot domain partitioning
|
% Plot domain partitioning
|
||||||
obj = obj.plotPartitions();
|
obj = obj.plotPartitions();
|
||||||
|
|
||||||
|
% Plot agent trails
|
||||||
|
obj = obj.plotTrails();
|
||||||
|
|
||||||
% Enforce plot limits
|
% Enforce plot limits
|
||||||
for ii = 1:size(obj.spatialPlotIndices, 2)
|
for ii = 1:size(obj.spatialPlotIndices, 2)
|
||||||
xlim(obj.f.Children(1).Children(obj.spatialPlotIndices(ii)), [obj.domain.minCorner(1), obj.domain.maxCorner(1)]);
|
xlim(obj.f.Children(1).Children(obj.spatialPlotIndices(ii)), [obj.domain.minCorner(1), obj.domain.maxCorner(1)]);
|
||||||
@@ -40,4 +48,7 @@ function obj = plot(obj)
|
|||||||
|
|
||||||
% Plot performance
|
% Plot performance
|
||||||
obj = obj.plotPerformance();
|
obj = obj.plotPerformance();
|
||||||
|
|
||||||
|
% Plot h functions
|
||||||
|
obj = obj.plotH();
|
||||||
end
|
end
|
||||||
@@ -9,9 +9,9 @@ function obj = plotConnections(obj)
|
|||||||
% Iterate over lower triangle off-diagonal region of the
|
% Iterate over lower triangle off-diagonal region of the
|
||||||
% adjacency matrix to plot communications links between agents
|
% adjacency matrix to plot communications links between agents
|
||||||
X = []; Y = []; Z = [];
|
X = []; Y = []; Z = [];
|
||||||
for ii = 2:size(obj.adjacency, 1)
|
for ii = 2:size(obj.constraintAdjacencyMatrix, 1)
|
||||||
for jj = 1:(ii - 1)
|
for jj = 1:(ii - 1)
|
||||||
if obj.adjacency(ii, jj)
|
if obj.constraintAdjacencyMatrix(ii, jj)
|
||||||
X = [X; obj.agents{ii}.pos(1), obj.agents{jj}.pos(1)];
|
X = [X; obj.agents{ii}.pos(1), obj.agents{jj}.pos(1)];
|
||||||
Y = [Y; obj.agents{ii}.pos(2), obj.agents{jj}.pos(2)];
|
Y = [Y; obj.agents{ii}.pos(2), obj.agents{jj}.pos(2)];
|
||||||
Z = [Z; obj.agents{ii}.pos(3), obj.agents{jj}.pos(3)];
|
Z = [Z; obj.agents{ii}.pos(3), obj.agents{jj}.pos(3)];
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ function obj = plotGraph(obj)
|
|||||||
end
|
end
|
||||||
|
|
||||||
% Form graph from adjacency matrix
|
% Form graph from adjacency matrix
|
||||||
G = graph(obj.adjacency, 'omitselfloops');
|
G = graph(obj.constraintAdjacencyMatrix, 'omitselfloops');
|
||||||
|
|
||||||
% Plot graph object
|
% Plot graph object
|
||||||
if isnan(obj.networkGraphIndex)
|
if isnan(obj.networkGraphIndex)
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
function obj = plotH(obj)
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'miSim')};
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'miSim')};
|
||||||
|
end
|
||||||
|
|
||||||
|
obj.hf = figure;
|
||||||
|
tiledlayout(obj.hf, 4, 1, "TileSpacing", "tight", "Padding", "compact");
|
||||||
|
|
||||||
|
nexttile(obj.hf.Children(1));
|
||||||
|
axes(obj.hf.Children(1).Children(1));
|
||||||
|
grid(obj.hf.Children(1).Children(1), "on");
|
||||||
|
xlabel(obj.hf.Children(1).Children(1), "Time (s)"); % ylabel(obj.hf.Children(1).Children(1), "");
|
||||||
|
title(obj.hf.Children(1).Children(1), "Collision Avoidance");
|
||||||
|
hold(obj.hf.Children(1).Children(1), "on");
|
||||||
|
obj.caPlot = plot(obj.h(1:(size(obj.agents, 1) * (size(obj.agents, 1) - 1) / 2), :)');
|
||||||
|
legendStrings = [];
|
||||||
|
for ii = 2:size(obj.agents, 1)
|
||||||
|
for jj = 1:(ii - 1)
|
||||||
|
legendStrings = [legendStrings; sprintf("A%d A%d", jj, ii)];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
legend(obj.hf.Children(1).Children(1), legendStrings, 'Location', 'bestoutside');
|
||||||
|
hold(obj.hf.Children(1).Children(2), "off");
|
||||||
|
|
||||||
|
nexttile(obj.hf.Children(1));
|
||||||
|
axes(obj.hf.Children(1).Children(1));
|
||||||
|
grid(obj.hf.Children(1).Children(1), "on");
|
||||||
|
xlabel(obj.hf.Children(1).Children(1), "Time (s)"); % ylabel(obj.hf.Children(1).Children(2), "");
|
||||||
|
title(obj.hf.Children(1).Children(1), "Obstacles");
|
||||||
|
hold(obj.hf.Children(1).Children(1), "on");
|
||||||
|
obj.obsPlot = plot(obj.h((1 + (size(obj.agents, 1) * (size(obj.agents, 1) - 1) / 2)):(((size(obj.agents, 1) * (size(obj.agents, 1) - 1) / 2)) + size(obj.agents, 1) * size(obj.obstacles, 1)), :)');
|
||||||
|
legendStrings = [];
|
||||||
|
for ii = 1:size(obj.obstacles, 1)
|
||||||
|
for jj = 1:size(obj.agents, 1)
|
||||||
|
legendStrings = [legendStrings; sprintf("A%d O%d", jj, ii)];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
legend(obj.hf.Children(1).Children(1), legendStrings, 'Location', 'bestoutside');
|
||||||
|
hold(obj.hf.Children(1).Children(2), "off");
|
||||||
|
|
||||||
|
nexttile(obj.hf.Children(1));
|
||||||
|
axes(obj.hf.Children(1).Children(1));
|
||||||
|
grid(obj.hf.Children(1).Children(1), "on");
|
||||||
|
xlabel(obj.hf.Children(1).Children(1), "Time (s)"); % ylabel(obj.hf.Children(1).Children(1), "");
|
||||||
|
title(obj.hf.Children(1).Children(1), "Domain");
|
||||||
|
hold(obj.hf.Children(1).Children(1), "on");
|
||||||
|
obj.domPlot = plot(obj.h((1 + (((size(obj.agents, 1) * (size(obj.agents, 1) - 1) / 2)) + size(obj.agents, 1) * size(obj.obstacles, 1))):size(obj.h, 1), 1:end)');
|
||||||
|
legend(obj.hf.Children(1).Children(1), ["X Min"; "X Max"; "Y Min"; "Y Max"; "Z Min"; "Z Max";], 'Location', 'bestoutside');
|
||||||
|
hold(obj.hf.Children(1).Children(2), "off");
|
||||||
|
|
||||||
|
nexttile(obj.hf.Children(1));
|
||||||
|
axes(obj.hf.Children(1).Children(1));
|
||||||
|
grid(obj.hf.Children(1).Children(1), "on");
|
||||||
|
xlabel(obj.hf.Children(1).Children(1), "Time (s)"); % ylabel(obj.hf.Children(1).Children(1), "");
|
||||||
|
title(obj.hf.Children(1).Children(1), "Communications");
|
||||||
|
% skipped this for now because it is very complicated
|
||||||
|
|
||||||
|
end
|
||||||
@@ -6,6 +6,13 @@ function obj = plotPerformance(obj)
|
|||||||
obj (1, 1) {mustBeA(obj, 'miSim')};
|
obj (1, 1) {mustBeA(obj, 'miSim')};
|
||||||
end
|
end
|
||||||
|
|
||||||
|
% fast exit when plotting is disabled
|
||||||
|
if ~obj.makePlots
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
obj.fPerf = figure;
|
||||||
|
|
||||||
axes(obj.fPerf);
|
axes(obj.fPerf);
|
||||||
title(obj.fPerf.Children(1), "Sensor Performance");
|
title(obj.fPerf.Children(1), "Sensor Performance");
|
||||||
xlabel(obj.fPerf.Children(1), 'Time (s)');
|
xlabel(obj.fPerf.Children(1), 'Time (s)');
|
||||||
@@ -15,14 +22,24 @@ function obj = plotPerformance(obj)
|
|||||||
% Plot current cumulative performance
|
% Plot current cumulative performance
|
||||||
hold(obj.fPerf.Children(1), 'on');
|
hold(obj.fPerf.Children(1), 'on');
|
||||||
o = plot(obj.fPerf.Children(1), obj.perf(end, :));
|
o = plot(obj.fPerf.Children(1), obj.perf(end, :));
|
||||||
|
warning('off', 'MATLAB:gui:array:InvalidArrayShape'); % suppress this warning to avoid polluting output
|
||||||
|
o.XData = NaN(1, obj.maxIter); % correct time will be set at runtime
|
||||||
|
o.YData = [0, NaN(1, obj.maxIter - 1)];
|
||||||
hold(obj.fPerf.Children(1), 'off');
|
hold(obj.fPerf.Children(1), 'off');
|
||||||
|
|
||||||
% Plot current agent performance
|
% Plot current agent performance
|
||||||
for ii = 1:(size(obj.perf, 1) - 1)
|
for ii = 1:(size(obj.perf, 1) - 1)
|
||||||
hold(obj.fPerf.Children(1), 'on');
|
hold(obj.fPerf.Children(1), 'on');
|
||||||
o = [o; plot(obj.fPerf.Children(1), obj.perf(ii, :))];
|
o = [o; plot(obj.fPerf.Children(1), obj.perf(ii, :))];
|
||||||
|
o(end).XData = NaN(1, obj.maxIter); % correct time will be set at runtime
|
||||||
|
o(end).YData = [0, NaN(1, obj.maxIter - 1)];
|
||||||
hold(obj.fPerf.Children(1), 'off');
|
hold(obj.fPerf.Children(1), 'off');
|
||||||
end
|
end
|
||||||
|
|
||||||
|
% Add legend
|
||||||
|
agentStrings = string(cellfun(@(x) x.label, obj.agents, 'UniformOutput', false));
|
||||||
|
agentStrings = ["Total"; agentStrings];
|
||||||
|
legend(obj.fPerf.Children(1), agentStrings, 'Location', 'northwest');
|
||||||
|
|
||||||
obj.performancePlot = o;
|
obj.performancePlot = o;
|
||||||
end
|
end
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
function obj = plotTrails(obj)
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'miSim')}
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'miSim')}
|
||||||
|
end
|
||||||
|
|
||||||
|
% fast exit when plotting is disabled
|
||||||
|
if ~obj.makePlots
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
% Plot full range of position history on each spatial plot axes
|
||||||
|
o = [];
|
||||||
|
for ii = 1:(size(obj.posHist, 1))
|
||||||
|
hold(obj.f.Children(1).Children(obj.spatialPlotIndices(1)), 'on');
|
||||||
|
o = [o; plot3(obj.f.Children(1).Children(obj.spatialPlotIndices(1)), obj.posHist(ii, 1:obj.maxIter, 1), obj.posHist(ii, 1:obj.maxIter, 2), obj.posHist(ii, 1:obj.maxIter, 3), 'Color', 'k', 'LineWidth', 1)];
|
||||||
|
hold(obj.f.Children(1).Children(obj.spatialPlotIndices(1)), 'off');
|
||||||
|
end
|
||||||
|
|
||||||
|
% Copy trails to other figures?
|
||||||
|
obj.trailPlot = o;
|
||||||
|
|
||||||
|
% Add legend?
|
||||||
|
end
|
||||||
+35
-10
@@ -7,14 +7,20 @@ function [obj] = run(obj)
|
|||||||
end
|
end
|
||||||
|
|
||||||
% Start video writer
|
% Start video writer
|
||||||
v = obj.setupVideoWriter();
|
if obj.makeVideo
|
||||||
v.open();
|
v = obj.setupVideoWriter();
|
||||||
|
v.open();
|
||||||
|
end
|
||||||
|
|
||||||
for ii = 1:size(obj.times, 1)
|
for ii = 1:size(obj.times, 1)
|
||||||
% Display current sim time
|
% Display current sim time
|
||||||
obj.t = obj.times(ii);
|
obj.t = obj.times(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);
|
||||||
|
|
||||||
|
% Validate current simulation configuration
|
||||||
|
obj.validate();
|
||||||
|
|
||||||
% Check if it's time for new partitions
|
% Check if it's time for new partitions
|
||||||
updatePartitions = false;
|
updatePartitions = false;
|
||||||
if ismember(obj.t, obj.partitioningTimes)
|
if ismember(obj.t, obj.partitioningTimes)
|
||||||
@@ -22,11 +28,26 @@ function [obj] = run(obj)
|
|||||||
obj = obj.partition();
|
obj = obj.partition();
|
||||||
end
|
end
|
||||||
|
|
||||||
% Iterate over agents to simulate their motion
|
% Determine desired communications links
|
||||||
|
obj = obj.lesserNeighbor();
|
||||||
|
|
||||||
|
% 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.objective, obj.domain, obj.partitioning);
|
obj.agents{jj} = obj.agents{jj}.run(obj.domain, obj.partitioning, obj.t, jj);
|
||||||
end
|
end
|
||||||
|
|
||||||
|
% Adjust motion determined by unconstrained gradient ascent using
|
||||||
|
% CBF constraints solved by QP
|
||||||
|
obj = constrainMotion(obj);
|
||||||
|
|
||||||
|
% Finished simulation for this timestep, do accounting
|
||||||
|
|
||||||
|
% 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);
|
||||||
|
|
||||||
|
% Update total performance
|
||||||
|
obj.performance = [obj.performance, sum(cellfun(@(x) x.performance(end), obj.agents))];
|
||||||
|
|
||||||
% Update adjacency matrix
|
% Update adjacency matrix
|
||||||
obj = obj.updateAdjacency();
|
obj = obj.updateAdjacency();
|
||||||
|
|
||||||
@@ -34,10 +55,14 @@ function [obj] = run(obj)
|
|||||||
obj = obj.updatePlots(updatePartitions);
|
obj = obj.updatePlots(updatePartitions);
|
||||||
|
|
||||||
% Write frame in to video
|
% Write frame in to video
|
||||||
I = getframe(obj.f);
|
if obj.makeVideo
|
||||||
v.writeVideo(I);
|
I = getframe(obj.f);
|
||||||
|
v.writeVideo(I);
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
% Close video file
|
if obj.makeVideo
|
||||||
v.close();
|
% Close video file
|
||||||
end
|
v.close();
|
||||||
|
end
|
||||||
|
end
|
||||||
+17
-14
@@ -7,26 +7,29 @@ function obj = updateAdjacency(obj)
|
|||||||
end
|
end
|
||||||
|
|
||||||
% Initialize assuming only self-connections
|
% Initialize assuming only self-connections
|
||||||
A = logical(eye(size(obj.agents, 1)));
|
A = true(size(obj.agents, 1));
|
||||||
|
|
||||||
% Check lower triangle off-diagonal connections
|
% Check lower triangle off-diagonal connections
|
||||||
for ii = 2:size(A, 1)
|
for ii = 2:size(A, 1)
|
||||||
for jj = 1:(ii - 1)
|
for jj = 1:(ii - 1)
|
||||||
if norm(obj.agents{ii}.pos - obj.agents{jj}.pos) <= min([obj.agents{ii}.comRange, obj.agents{jj}.comRange])
|
% Check that agents are not out of range
|
||||||
% Make sure that obstacles don't obstruct the line
|
if norm(obj.agents{ii}.pos - obj.agents{jj}.pos) > min([obj.agents{ii}.commsGeometry.radius, obj.agents{jj}.commsGeometry.radius])
|
||||||
% of sight, breaking the connection
|
A(ii, jj) = false; % comm range violation
|
||||||
for kk = 1:size(obj.obstacles, 1)
|
continue;
|
||||||
if ~obj.obstacles{kk}.containsLine(obj.agents{ii}.pos, obj.agents{jj}.pos)
|
|
||||||
A(ii, jj) = true;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
% need extra handling for cases with no obstacles
|
|
||||||
if isempty(obj.obstacles)
|
|
||||||
A(ii, jj) = true;
|
|
||||||
end
|
|
||||||
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
|
||||||
+33
-11
@@ -7,13 +7,18 @@ function [obj] = updatePlots(obj, updatePartitions)
|
|||||||
obj (1, 1) {mustBeA(obj, 'miSim')};
|
obj (1, 1) {mustBeA(obj, 'miSim')};
|
||||||
end
|
end
|
||||||
|
|
||||||
% Update agent positions, collision geometries
|
% Fast exit when plotting is disabled
|
||||||
|
if ~obj.makePlots
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
% Update agent positions, collision/communication geometries
|
||||||
for ii = 1:size(obj.agents, 1)
|
for ii = 1:size(obj.agents, 1)
|
||||||
obj.agents{ii}.updatePlots();
|
obj.agents{ii}.updatePlots();
|
||||||
end
|
end
|
||||||
|
|
||||||
% The remaining updates might be possible to do in a clever way
|
% The remaining updates might should all be possible to do in a clever
|
||||||
% that moves existing lines instead of clearing and
|
% way that moves existing lines instead of clearing and
|
||||||
% re-plotting, which is much better for performance boost
|
% re-plotting, which is much better for performance boost
|
||||||
|
|
||||||
% Update agent connections plot
|
% Update agent connections plot
|
||||||
@@ -36,17 +41,34 @@ function [obj] = updatePlots(obj, updatePartitions)
|
|||||||
ylim(obj.f.Children(1).Children(obj.spatialPlotIndices(ii)), [obj.domain.minCorner(2), obj.domain.maxCorner(2)]);
|
ylim(obj.f.Children(1).Children(obj.spatialPlotIndices(ii)), [obj.domain.minCorner(2), obj.domain.maxCorner(2)]);
|
||||||
zlim(obj.f.Children(1).Children(obj.spatialPlotIndices(ii)), [obj.domain.minCorner(3), obj.domain.maxCorner(3)]);
|
zlim(obj.f.Children(1).Children(obj.spatialPlotIndices(ii)), [obj.domain.minCorner(3), obj.domain.maxCorner(3)]);
|
||||||
end
|
end
|
||||||
|
|
||||||
|
% Update agent trails
|
||||||
|
for ii = 1:size(obj.agents, 1)
|
||||||
|
obj.trailPlot(ii).XData(obj.timestepIndex) = obj.posHist(ii, obj.timestepIndex, 1);
|
||||||
|
obj.trailPlot(ii).YData(obj.timestepIndex) = obj.posHist(ii, obj.timestepIndex, 2);
|
||||||
|
obj.trailPlot(ii).ZData(obj.timestepIndex) = obj.posHist(ii, obj.timestepIndex, 3);
|
||||||
|
end
|
||||||
|
|
||||||
drawnow;
|
drawnow;
|
||||||
|
|
||||||
% Update performance plot
|
% Update performance plot
|
||||||
if updatePartitions
|
% Re-normalize performance plot
|
||||||
nowIdx = [0; obj.partitioningTimes] == obj.t;
|
normalizingFactor = 1/max(obj.performance(end));
|
||||||
% set(obj.performancePlot(1), 'YData', obj.perf(end, 1:find(nowIdx)));
|
obj.performancePlot(1).YData(1:length(obj.performance)) = obj.performance * normalizingFactor;
|
||||||
obj.performancePlot(1).YData(nowIdx) = obj.perf(end, nowIdx);
|
obj.performancePlot(1).XData(obj.timestepIndex) = obj.t;
|
||||||
for ii = 2:size(obj.performancePlot, 1)
|
for ii = 2:(size(obj.agents, 1) + 1)
|
||||||
obj.performancePlot(ii).YData(nowIdx) = obj.perf(ii, nowIdx);
|
obj.performancePlot(ii).YData(1:length(obj.performance)) = obj.agents{ii - 1}.performance * normalizingFactor;
|
||||||
end
|
obj.performancePlot(ii).XData(obj.timestepIndex) = obj.t;
|
||||||
drawnow;
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
% Update h function plots
|
||||||
|
for ii = 1:size(obj.caPlot, 1)
|
||||||
|
obj.caPlot(ii).YData(obj.timestepIndex) = obj.h(ii, obj.timestepIndex);
|
||||||
|
end
|
||||||
|
for ii = 1:size(obj.obsPlot, 1)
|
||||||
|
obj.obsPlot(ii).YData(obj.timestepIndex) = obj.h(ii + size(obj.caPlot, 1), obj.timestepIndex);
|
||||||
|
end
|
||||||
|
for ii = 1:size(obj.domPlot, 1)
|
||||||
|
obj.domPlot(ii).YData(obj.timestepIndex) = obj.h(ii + size(obj.caPlot, 1) + size(obj.obsPlot, 1), obj.timestepIndex);
|
||||||
|
end
|
||||||
end
|
end
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
function validate(obj)
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'miSim')};
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
end
|
||||||
|
|
||||||
|
if max(conncomp(graph(obj.adjacency))) ~= 1
|
||||||
|
warning("Network is not connected");
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -10,6 +10,8 @@ function obj = initialize(obj, objectiveFunction, domain, discretizationStep, pr
|
|||||||
obj (1,1) {mustBeA(obj, 'sensingObjective')};
|
obj (1,1) {mustBeA(obj, 'sensingObjective')};
|
||||||
end
|
end
|
||||||
|
|
||||||
|
obj.discretizationStep = discretizationStep;
|
||||||
|
|
||||||
obj.groundAlt = domain.minCorner(3);
|
obj.groundAlt = domain.minCorner(3);
|
||||||
obj.protectedRange = protectedRange;
|
obj.protectedRange = protectedRange;
|
||||||
|
|
||||||
@@ -19,8 +21,8 @@ function obj = initialize(obj, objectiveFunction, domain, discretizationStep, pr
|
|||||||
yMin = min(domain.footprint(:, 2));
|
yMin = min(domain.footprint(:, 2));
|
||||||
yMax = max(domain.footprint(:, 2));
|
yMax = max(domain.footprint(:, 2));
|
||||||
|
|
||||||
xGrid = unique([xMin:discretizationStep:xMax, xMax]);
|
xGrid = unique([xMin:obj.discretizationStep:xMax, xMax]);
|
||||||
yGrid = unique([yMin:discretizationStep:yMax, yMax]);
|
yGrid = unique([yMin:obj.discretizationStep:yMax, yMax]);
|
||||||
|
|
||||||
% Store grid points for plotting later
|
% Store grid points for plotting later
|
||||||
[obj.X, obj.Y] = meshgrid(xGrid, yGrid);
|
[obj.X, obj.Y] = meshgrid(xGrid, yGrid);
|
||||||
@@ -28,10 +30,14 @@ function obj = initialize(obj, objectiveFunction, domain, discretizationStep, pr
|
|||||||
% Evaluate function over grid points
|
% Evaluate function over grid points
|
||||||
obj.objectiveFunction = objectiveFunction;
|
obj.objectiveFunction = objectiveFunction;
|
||||||
obj.values = reshape(obj.objectiveFunction(obj.X, obj.Y), size(obj.X));
|
obj.values = reshape(obj.objectiveFunction(obj.X, obj.Y), size(obj.X));
|
||||||
|
|
||||||
|
% Normalize
|
||||||
|
obj.values = obj.values ./ max(obj.values, [], "all");
|
||||||
|
|
||||||
% store ground position
|
% store ground position
|
||||||
idx = obj.values == max(obj.values, [], "all");
|
idx = obj.values == 1;
|
||||||
obj.groundPos = [obj.X(idx), obj.Y(idx)];
|
obj.groundPos = [obj.X(idx), obj.Y(idx)];
|
||||||
|
obj.groundPos = obj.groundPos(1, 1:2); % for safety, in case 2 points are maximal (somehow)
|
||||||
|
|
||||||
assert(domain.distance([obj.groundPos, domain.center(3)]) > protectedRange, "Domain is crowding the sensing objective")
|
assert(domain.distance([obj.groundPos, domain.center(3)]) > protectedRange, "Domain is crowding the sensing objective")
|
||||||
end
|
end
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
function cPos = closestToPoint(obj, pos)
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'rectangularPrism')};
|
||||||
|
pos (:, 3) double;
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
cPos (:, 3) double;
|
||||||
|
end
|
||||||
|
cPos = NaN(1, 3);
|
||||||
|
for ii = 1:3
|
||||||
|
if pos(ii) < obj.minCorner(ii)
|
||||||
|
cPos(ii) = obj.minCorner(ii);
|
||||||
|
elseif pos(ii) > obj.maxCorner(ii)
|
||||||
|
cPos(ii) = obj.maxCorner(ii);
|
||||||
|
else
|
||||||
|
cPos(ii) = pos(ii);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -9,33 +9,38 @@ function c = containsLine(obj, pos1, pos2)
|
|||||||
end
|
end
|
||||||
|
|
||||||
d = pos2 - pos1;
|
d = pos2 - pos1;
|
||||||
|
|
||||||
% edge case where the line is parallel to the geometry
|
% endpoint contained (trivial case)
|
||||||
if abs(d) < 1e-12
|
if obj.contains(pos1) || obj.contains(pos2)
|
||||||
% check if it happens to start or end inside or outside of
|
c = true;
|
||||||
% the geometry
|
|
||||||
if obj.contains(pos1) || obj.contains(pos2)
|
|
||||||
c = true;
|
|
||||||
else
|
|
||||||
c = false;
|
|
||||||
end
|
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
tmin = -inf;
|
% parameterize the line segment to check for an intersection
|
||||||
tmax = inf;
|
tMin = 0;
|
||||||
|
tMax = 1;
|
||||||
% Standard case
|
|
||||||
for ii = 1:3
|
for ii = 1:3
|
||||||
t1 = (obj.minCorner(ii) - pos1(ii)) / d(ii);
|
% line is parallel to geometry
|
||||||
t2 = (obj.maxCorner(ii) - pos2(ii)) / d(ii);
|
if abs(d(ii)) < 1e-12
|
||||||
tmin = max(tmin, min(t1, t2));
|
if pos1(ii) < obj.minCorner(ii) || pos1(ii) > obj.maxCorner(ii)
|
||||||
tmax = min(tmax, max(t1, t2));
|
c = false;
|
||||||
if tmin > tmax
|
return;
|
||||||
c = false;
|
end
|
||||||
return;
|
else
|
||||||
|
t1 = (obj.minCorner(ii) - pos1(ii)) / d(ii);
|
||||||
|
t2 = (obj.maxCorner(ii) - pos1(ii)) / d(ii);
|
||||||
|
|
||||||
|
tLow = min(t1, t2);
|
||||||
|
tHigh = max(t1, t2);
|
||||||
|
|
||||||
|
tMin = max(tMin, tLow);
|
||||||
|
tMax = min(tMax, tHigh);
|
||||||
|
|
||||||
|
if tMin > tMax
|
||||||
|
c = false;
|
||||||
|
return;
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
c = true;
|
||||||
c = (tmax >= 0) && (tmin <= 1);
|
end
|
||||||
end
|
|
||||||
@@ -4,7 +4,7 @@ function d = distance(obj, pos)
|
|||||||
pos (:, 3) double;
|
pos (:, 3) double;
|
||||||
end
|
end
|
||||||
arguments (Output)
|
arguments (Output)
|
||||||
d (:, 1) double
|
d (:, 1) double;
|
||||||
end
|
end
|
||||||
if obj.contains(pos)
|
if obj.contains(pos)
|
||||||
% Queried point is inside geometry
|
% Queried point is inside geometry
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
function g = distanceGradient(obj, pos)
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'rectangularPrism')};
|
||||||
|
pos (:, 3) double;
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
g (:, 3) double
|
||||||
|
end
|
||||||
|
|
||||||
|
% find nearest point on surface to query position
|
||||||
|
q = min(max(pos, obj.minCorner), obj.maxCorner);
|
||||||
|
|
||||||
|
% Find distance and direction between pos and q
|
||||||
|
v = pos - q;
|
||||||
|
vNorm = norm(v);
|
||||||
|
|
||||||
|
% position is outside geometry
|
||||||
|
if vNorm > 0
|
||||||
|
% gradient is normalized vector from q to p
|
||||||
|
g = v / vNorm;
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
% position is on or in geometry
|
||||||
|
% find distances to each face in each dimension
|
||||||
|
distances = [pos(1) - obj.minCorner(1), obj.maxCorner(1) - pos(1), pos(2) - obj.minCorner(2), obj.maxCorner(2) - pos(2), pos(3) - obj.minCorner(3), obj.maxCorner(3) - pos(3)];
|
||||||
|
[~, idx] = min(distances);
|
||||||
|
|
||||||
|
% I think there needs to be additional handling here for the
|
||||||
|
% edge/corner cases, where there are ways to balance or resolve ties
|
||||||
|
% when two faces are equidistant to the query position
|
||||||
|
assert(sum(idx) == idx, "Implement edge case handling");
|
||||||
|
|
||||||
|
% select gradient that brings us quickest to the nearest face
|
||||||
|
g = [ 1, 0, 0; ...
|
||||||
|
-1, 0, 0; ...
|
||||||
|
0, 1, 0; ...
|
||||||
|
0, -1, 0; ...
|
||||||
|
0, 0, 1; ...
|
||||||
|
0, 0, -1;];
|
||||||
|
g = g(idx, :);
|
||||||
|
end
|
||||||
@@ -24,6 +24,10 @@ function obj = initialize(obj, bounds, tag, label, objectiveFunction, discretiza
|
|||||||
% Compute center
|
% Compute center
|
||||||
obj.center = obj.minCorner + obj.dimensions ./ 2;
|
obj.center = obj.minCorner + obj.dimensions ./ 2;
|
||||||
|
|
||||||
|
% Compute a (fake) radius
|
||||||
|
% fully contains the rectangular prism from the center
|
||||||
|
obj.radius = (1/2) * sqrt(sum(obj.dimensions.^2));
|
||||||
|
|
||||||
% Compute vertices
|
% Compute vertices
|
||||||
obj.vertices = [obj.minCorner;
|
obj.vertices = [obj.minCorner;
|
||||||
obj.maxCorner(1), obj.minCorner(2:3);
|
obj.maxCorner(1), obj.minCorner(2:3);
|
||||||
@@ -44,4 +48,13 @@ 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
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
function [obj] = initializeRandom(obj, tag, label, minDimension, maxDimension, domain)
|
function [obj] = initializeRandom(obj, tag, label, minDimension, maxDimension, domain, minAlt)
|
||||||
arguments (Input)
|
arguments (Input)
|
||||||
obj (1, 1) {mustBeA(obj, 'rectangularPrism')};
|
obj (1, 1) {mustBeA(obj, 'rectangularPrism')};
|
||||||
tag (1, 1) REGION_TYPE = REGION_TYPE.INVALID;
|
tag (1, 1) REGION_TYPE = REGION_TYPE.INVALID;
|
||||||
label (1, 1) string = "";
|
label (1, 1) string = "";
|
||||||
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;
|
||||||
end
|
end
|
||||||
arguments (Output)
|
arguments (Output)
|
||||||
obj (1, 1) {mustBeA(obj, 'rectangularPrism')};
|
obj (1, 1) {mustBeA(obj, 'rectangularPrism')};
|
||||||
@@ -27,7 +28,7 @@ function [obj] = initializeRandom(obj, tag, label, minDimension, maxDimension, d
|
|||||||
while ~domain.contains(candidateMaxCorner) || all(domain.objective.groundPos + domain.objective.protectedRange >= candidateMinCorner(1:2), 2) && all(domain.objective.groundPos - domain.objective.protectedRange <= candidateMaxCorner(1:2), 2)
|
while ~domain.contains(candidateMaxCorner) || all(domain.objective.groundPos + domain.objective.protectedRange >= candidateMinCorner(1:2), 2) && all(domain.objective.groundPos - domain.objective.protectedRange <= candidateMaxCorner(1:2), 2)
|
||||||
if ii == 0 || ii > 10
|
if ii == 0 || ii > 10
|
||||||
candidateMinCorner = domain.random();
|
candidateMinCorner = domain.random();
|
||||||
candidateMinCorner(3) = 0; % bind to floor
|
candidateMinCorner(3) = minAlt; % bind to floor (plus minimum altitude constraint)
|
||||||
ii = 1;
|
ii = 1;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ classdef rectangularPrism
|
|||||||
properties (SetAccess = private, GetAccess = public)
|
properties (SetAccess = private, GetAccess = public)
|
||||||
% Meta
|
% Meta
|
||||||
tag = REGION_TYPE.INVALID;
|
tag = REGION_TYPE.INVALID;
|
||||||
label = "";
|
|
||||||
|
|
||||||
% Spatial
|
% Spatial
|
||||||
minCorner = NaN(1, 3);
|
minCorner = NaN(1, 3);
|
||||||
@@ -11,6 +10,7 @@ classdef rectangularPrism
|
|||||||
dimensions = NaN(1, 3);
|
dimensions = NaN(1, 3);
|
||||||
center = NaN;
|
center = NaN;
|
||||||
footprint = NaN(4, 2);
|
footprint = NaN(4, 2);
|
||||||
|
radius = NaN; % fake radius
|
||||||
|
|
||||||
% Graph
|
% Graph
|
||||||
vertices = NaN(8, 3);
|
vertices = NaN(8, 3);
|
||||||
@@ -20,8 +20,13 @@ classdef rectangularPrism
|
|||||||
|
|
||||||
% Plotting
|
% Plotting
|
||||||
lines;
|
lines;
|
||||||
|
|
||||||
|
% collision
|
||||||
|
barrierFunction;
|
||||||
|
dBarrierFunction;
|
||||||
end
|
end
|
||||||
properties (SetAccess = public, GetAccess = public)
|
properties (SetAccess = public, GetAccess = public)
|
||||||
|
label = "";
|
||||||
% Sensing objective (for DOMAIN region type only)
|
% Sensing objective (for DOMAIN region type only)
|
||||||
objective;
|
objective;
|
||||||
end
|
end
|
||||||
@@ -31,7 +36,9 @@ classdef rectangularPrism
|
|||||||
[obj ] = initializeRandom(obj, tag, label, minDimension, maxDimension, domain);
|
[obj ] = initializeRandom(obj, tag, label, minDimension, maxDimension, domain);
|
||||||
[r ] = random(obj);
|
[r ] = random(obj);
|
||||||
[c ] = contains(obj, pos);
|
[c ] = contains(obj, pos);
|
||||||
|
[cPos ] = closestToPoint(obj, pos);
|
||||||
[d ] = distance(obj, pos);
|
[d ] = distance(obj, pos);
|
||||||
|
[g ] = distanceGradient(obj, pos);
|
||||||
[c ] = containsLine(obj, pos1, pos2);
|
[c ] = containsLine(obj, pos1, pos2);
|
||||||
[obj, f] = plotWireframe(obj, ind, f);
|
[obj, f] = plotWireframe(obj, ind, f);
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
function c = contains(obj, pos)
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'spherical')};
|
||||||
|
pos (:, 3) double;
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
c (:, 1) logical
|
||||||
|
end
|
||||||
|
c = norm(obj.center - pos) <= obj.radius;
|
||||||
|
end
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
function c = containsLine(obj, pos1, pos2)
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'spherical')};
|
||||||
|
pos1 (1, 3) double;
|
||||||
|
pos2 (1, 3) double;
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
c (1, 1) logical
|
||||||
|
end
|
||||||
|
|
||||||
|
d = pos2 - pos1;
|
||||||
|
f = pos1 - obj.center;
|
||||||
|
|
||||||
|
a = dot(d, d);
|
||||||
|
b = 2 * dot(f, d);
|
||||||
|
c = dot(f, f) - obj.radius^2;
|
||||||
|
|
||||||
|
disc = b^2 - 4*a*c;
|
||||||
|
|
||||||
|
if disc < 0
|
||||||
|
c = false;
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
t = [(-b - sqrt(disc)) / (2 * a), (-b + sqrt(disc)) / (2 * a)];
|
||||||
|
|
||||||
|
c = (t(1) >= 0 && t(1) <= 1) || (t(2) >= 0 && t(2) <= 1);
|
||||||
|
end
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
function obj = initialize(obj, center, radius, tag, label)
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'spherical')};
|
||||||
|
center (1, 3) double;
|
||||||
|
radius (1, 1) double;
|
||||||
|
tag (1, 1) REGION_TYPE = REGION_TYPE.INVALID;
|
||||||
|
label (1, 1) string = "";
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'spherical')};
|
||||||
|
end
|
||||||
|
|
||||||
|
obj.tag = tag;
|
||||||
|
obj.label = label;
|
||||||
|
|
||||||
|
% Define geometry
|
||||||
|
obj.center = center;
|
||||||
|
obj.radius = 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
|
||||||
|
obj.vertices = [obj.center + [obj.radius, 0, 0]; ...
|
||||||
|
obj.center - [obj.radius, 0, 0]; ...
|
||||||
|
obj.center + [0, obj.radius, 0]; ...
|
||||||
|
obj.center - [0, obj.radius, 0]; ...
|
||||||
|
obj.center + [0, 0, obj.radius]; ...
|
||||||
|
obj.center - [0, 0, obj.radius]];
|
||||||
|
% fake edges in two perpendicular rings
|
||||||
|
obj.edges = [1, 3; ...
|
||||||
|
3, 2; ...
|
||||||
|
2, 4; ...
|
||||||
|
4, 1; ...
|
||||||
|
1, 5; ...
|
||||||
|
5, 2; ...
|
||||||
|
2, 6; ...
|
||||||
|
6, 1];
|
||||||
|
end
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
function [obj, f] = plotWireframe(obj, ind, f)
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'spherical')};
|
||||||
|
ind (1, :) double = NaN;
|
||||||
|
f (1, 1) {mustBeA(f, 'matlab.ui.Figure')} = figure;
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'spherical')};
|
||||||
|
f (1, 1) {mustBeA(f, 'matlab.ui.Figure')};
|
||||||
|
end
|
||||||
|
|
||||||
|
% Create axes if they don't already exist
|
||||||
|
f = firstPlotSetup(f);
|
||||||
|
|
||||||
|
% Create plotting inputs
|
||||||
|
[X, Y, Z] = sphere(8);
|
||||||
|
% Scale
|
||||||
|
X = X * obj.radius;
|
||||||
|
Y = Y * obj.radius;
|
||||||
|
Z = Z * obj.radius;
|
||||||
|
% Shift
|
||||||
|
X = X + obj.center(1);
|
||||||
|
Y = Y + obj.center(2);
|
||||||
|
Z = Z + obj.center(3);
|
||||||
|
|
||||||
|
% Plot the boundaries of the geometry into 3D view
|
||||||
|
if isnan(ind)
|
||||||
|
o = plot3(f.CurrentAxes, X, Y, Z, '-', 'Color', obj.tag.color, 'LineWidth', 2);
|
||||||
|
else
|
||||||
|
hold(f.Children(1).Children(ind(1)), "on");
|
||||||
|
o = plot3(f.Children(1).Children(ind(1)), X, Y, Z, '-', 'Color', obj.tag.color, 'LineWidth', 2);
|
||||||
|
hold(f.Children(1).Children(ind(1)), "off");
|
||||||
|
end
|
||||||
|
|
||||||
|
% Copy to other requested tiles
|
||||||
|
if numel(ind) > 1
|
||||||
|
for ii = 2:size(ind, 2)
|
||||||
|
o = [o, copyobj(o(:, 1), f.Children(1).Children(ind(ii)))];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
obj.lines = o;
|
||||||
|
end
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
function r = random(obj)
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, 'spherical')};
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
r (1, 3) double
|
||||||
|
end
|
||||||
|
y = (rand - 0.5) * 2; % uniform draw on [-1, 1]
|
||||||
|
R = sqrt(1 - y^2);
|
||||||
|
lon = (rand - 0.5) * 2 * pi; % uniform draw on [-pi, pi]
|
||||||
|
s = [R * sin(lon), y, R * cos(lon)]; % random point on surface
|
||||||
|
r = s * rand^(1/3); % scaled to random normalized radius [0, 1]
|
||||||
|
|
||||||
|
r = obj.center + obj.radius * r;
|
||||||
|
end
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
classdef spherical
|
||||||
|
% Rectangular prism geometry
|
||||||
|
properties (SetAccess = private, GetAccess = public)
|
||||||
|
% Spatial
|
||||||
|
center = NaN;
|
||||||
|
radius = NaN;
|
||||||
|
diameter = NaN;
|
||||||
|
|
||||||
|
vertices; % fake vertices
|
||||||
|
edges; % fake edges
|
||||||
|
|
||||||
|
% Plotting
|
||||||
|
lines;
|
||||||
|
|
||||||
|
% collision
|
||||||
|
barrierFunction;
|
||||||
|
dBarrierFunction;
|
||||||
|
end
|
||||||
|
properties (SetAccess = public, GetAccess = public)
|
||||||
|
% Meta
|
||||||
|
tag = REGION_TYPE.INVALID;
|
||||||
|
label = "";
|
||||||
|
|
||||||
|
% Sensing objective (for DOMAIN region type only)
|
||||||
|
objective;
|
||||||
|
end
|
||||||
|
|
||||||
|
methods (Access = public)
|
||||||
|
[obj ] = initialize(obj, center, radius, tag, label);
|
||||||
|
[r ] = random(obj);
|
||||||
|
[c ] = contains(obj, pos);
|
||||||
|
[d ] = distance(obj, pos);
|
||||||
|
[g ] = distanceGradient(obj, pos);
|
||||||
|
[c ] = containsLine(obj, pos1, pos2);
|
||||||
|
[obj, f] = plotWireframe(obj, ind, f);
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -9,6 +9,7 @@ classdef REGION_TYPE
|
|||||||
OBSTACLE (2, [255, 127, 127]); % obstacle region
|
OBSTACLE (2, [255, 127, 127]); % obstacle region
|
||||||
COLLISION (3, [255, 255, 128]); % collision avoidance region
|
COLLISION (3, [255, 255, 128]); % collision avoidance region
|
||||||
FOV (4, [255, 165, 0]); % field of view region
|
FOV (4, [255, 165, 0]); % field of view region
|
||||||
|
COMMS (5, [0, 255, 0]); % comunications region
|
||||||
end
|
end
|
||||||
methods
|
methods
|
||||||
function obj = REGION_TYPE(id, color)
|
function obj = REGION_TYPE(id, color)
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
function nextPos = gradientAscent(sensedValues, sensedPositions, pos, rate)
|
|
||||||
arguments (Input)
|
|
||||||
sensedValues (:, 1) double;
|
|
||||||
sensedPositions (:, 3) double;
|
|
||||||
pos (1, 3) double;
|
|
||||||
rate (1, 1) double = 0.1;
|
|
||||||
end
|
|
||||||
arguments (Output)
|
|
||||||
nextPos(1, 3) double;
|
|
||||||
end
|
|
||||||
|
|
||||||
% As a default, maintain current position
|
|
||||||
if size(sensedValues, 1) == 0 && size(sensedPositions, 1) == 0
|
|
||||||
nextPos = pos;
|
|
||||||
return;
|
|
||||||
end
|
|
||||||
|
|
||||||
% Select next position by maximum sensed value
|
|
||||||
nextPos = sensedPositions(sensedValues == max(sensedValues), :);
|
|
||||||
nextPos = [nextPos(1, 1:2), pos(3)]; % just in case two get selected, simply pick one
|
|
||||||
|
|
||||||
% rate-limit motion
|
|
||||||
v = nextPos - pos;
|
|
||||||
nextPos = pos + (v / norm(v, 2)) * rate;
|
|
||||||
|
|
||||||
end
|
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info Ref="sensingModels" Type="Relative"/>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info location="420d04e4-3880-4a45-8609-11cb30d87302" type="Reference"/>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info Ref="guidanceModels" Type="Relative"/>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info location="1d8d2b42-2863-4985-9cf2-980917971eba" type="Reference"/>
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info Ref="sensorModels" Type="Relative"/>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info location="d143c27d-6824-4569-9093-8150b60976cb" type="Reference"/>
|
||||||
File renamed without changes.
+1
-1
@@ -1,2 +1,2 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Info location="sense.m" type="File"/>
|
<Info location="random.m" type="File"/>
|
||||||
File renamed without changes.
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info location="containsLine.m" type="File"/>
|
||||||
File renamed without changes.
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info location="spherical.m" type="File"/>
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info location="contains.m" type="File"/>
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info location="plotWireframe.m" type="File"/>
|
||||||
File renamed without changes.
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info location="@spherical" type="File"/>
|
||||||
File renamed without changes.
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info location="plotTrails.m" type="File"/>
|
||||||
File renamed without changes.
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info location="lesserNeighbor.m" type="File"/>
|
||||||
File renamed without changes.
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info location="constrainMotion.m" type="File"/>
|
||||||
File renamed without changes.
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info location="sensorModels" type="File"/>
|
||||||
@@ -1,6 +1,2 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Info>
|
<Info/>
|
||||||
<Category UUID="FileClassCategory">
|
|
||||||
<Label UUID="design"/>
|
|
||||||
</Category>
|
|
||||||
</Info>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info location="sensingModels" type="File"/>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Info location="guidanceModels" type="File"/>
|
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info>
|
||||||
|
<Category UUID="FileClassCategory">
|
||||||
|
<Label UUID="design"/>
|
||||||
|
</Category>
|
||||||
|
</Info>
|
||||||
File renamed without changes.
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info>
|
||||||
|
<Category UUID="FileClassCategory">
|
||||||
|
<Label UUID="design"/>
|
||||||
|
</Category>
|
||||||
|
</Info>
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info>
|
||||||
|
<Category UUID="FileClassCategory">
|
||||||
|
<Label UUID="design"/>
|
||||||
|
</Category>
|
||||||
|
</Info>
|
||||||
File renamed without changes.
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info>
|
||||||
|
<Category UUID="FileClassCategory">
|
||||||
|
<Label UUID="design"/>
|
||||||
|
</Category>
|
||||||
|
</Info>
|
||||||
File renamed without changes.
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info>
|
||||||
|
<Category UUID="FileClassCategory">
|
||||||
|
<Label UUID="design"/>
|
||||||
|
</Category>
|
||||||
|
</Info>
|
||||||
File renamed without changes.
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info>
|
||||||
|
<Category UUID="FileClassCategory">
|
||||||
|
<Label UUID="design"/>
|
||||||
|
</Category>
|
||||||
|
</Info>
|
||||||
File renamed without changes.
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info>
|
||||||
|
<Category UUID="FileClassCategory">
|
||||||
|
<Label UUID="design"/>
|
||||||
|
</Category>
|
||||||
|
</Info>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Info location="initialize.m" type="File"/>
|
||||||
Loaded 100 of 119 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user