From afa5d79c1db9ceb688b2be01313d1ea1441c3c53 Mon Sep 17 00:00:00 2001 From: Kevin D Date: Sat, 15 Nov 2025 16:01:18 -0800 Subject: [PATCH] refactored sensing objective into domain, random inits --- @miSim/plot.m | 2 +- @sensingObjective/initialize.m | 15 +++-- @sensingObjective/initializeRandomMvnpdf.m | 27 ++++++++ @sensingObjective/plot.m | 1 - @sensingObjective/sensingObjective.m | 3 +- geometries/@rectangularPrism/initialize.m | 11 +++- .../@rectangularPrism/initializeRandom.m | 18 ++++++ .../@rectangularPrism/rectangularPrism.m | 7 ++- .../MwRvCS7aBsmP7sDYhggreAErsO8d.xml | 6 ++ .../MwRvCS7aBsmP7sDYhggreAErsO8p.xml | 2 + .../qyT5ovOLTSj0PEK6QQG5cW9EeTAd.xml | 6 ++ .../qyT5ovOLTSj0PEK6QQG5cW9EeTAp.xml | 2 + test/test_miSim.m | 61 ++++++++----------- 13 files changed, 110 insertions(+), 51 deletions(-) create mode 100644 @sensingObjective/initializeRandomMvnpdf.m create mode 100644 geometries/@rectangularPrism/initializeRandom.m create mode 100644 resources/project/qFRcMsci5bTUnTBevgtb2pITyQU/MwRvCS7aBsmP7sDYhggreAErsO8d.xml create mode 100644 resources/project/qFRcMsci5bTUnTBevgtb2pITyQU/MwRvCS7aBsmP7sDYhggreAErsO8p.xml create mode 100644 resources/project/tBBDcBPWdBTCTMSBG9IGJ_TsICk/qyT5ovOLTSj0PEK6QQG5cW9EeTAd.xml create mode 100644 resources/project/tBBDcBPWdBTCTMSBG9IGJ_TsICk/qyT5ovOLTSj0PEK6QQG5cW9EeTAp.xml diff --git a/@miSim/plot.m b/@miSim/plot.m index 243b67f..4fea9fa 100644 --- a/@miSim/plot.m +++ b/@miSim/plot.m @@ -16,7 +16,7 @@ function [obj, f] = plot(obj) end % Plot objective gradient - f = obj.objective.plot(obj.objectivePlotIndices, f); + f = obj.domain.objective.plot(obj.objectivePlotIndices, f); % Plot agents and their collision geometries for ii = 1:size(obj.agents, 1) diff --git a/@sensingObjective/initialize.m b/@sensingObjective/initialize.m index 1d51fb0..69c00eb 100644 --- a/@sensingObjective/initialize.m +++ b/@sensingObjective/initialize.m @@ -1,22 +1,21 @@ -function obj = initialize(obj, objectiveFunction, footprint, groundAlt, discretizationStep) +function obj = initialize(obj, objectiveFunction, domain, discretizationStep) arguments (Input) obj (1,1) {mustBeA(obj, 'sensingObjective')}; objectiveFunction (1, 1) {mustBeA(objectiveFunction, 'function_handle')}; - footprint (:, 2) double; - groundAlt (1, 1) double = 0; + domain (1, 1) {mustBeGeometry}; discretizationStep (1, 1) double = 1; end arguments (Output) obj (1,1) {mustBeA(obj, 'sensingObjective')}; end - obj.groundAlt = groundAlt; + obj.groundAlt = domain.minCorner(3); % Extract footprint limits - xMin = min(footprint(:, 1)); - xMax = max(footprint(:, 1)); - yMin = min(footprint(:, 2)); - yMax = max(footprint(:, 2)); + xMin = min(domain.footprint(:, 1)); + xMax = max(domain.footprint(:, 1)); + yMin = min(domain.footprint(:, 2)); + yMax = max(domain.footprint(:, 2)); xGrid = unique([xMin:discretizationStep:xMax, xMax]); yGrid = unique([yMin:discretizationStep:yMax, yMax]); diff --git a/@sensingObjective/initializeRandomMvnpdf.m b/@sensingObjective/initializeRandomMvnpdf.m new file mode 100644 index 0000000..b421acb --- /dev/null +++ b/@sensingObjective/initializeRandomMvnpdf.m @@ -0,0 +1,27 @@ +function obj = initializeRandomMvnpdf(obj, domain, protectedRange, discretizationStep) + arguments (Input) + obj (1, 1) {mustBeA(obj, 'sensingObjective')}; + domain (1, 1) {mustBeGeometry}; + protectedRange (1, 1) double = 1; + discretizationStep (1, 1) double = 1; + end + arguments (Output) + obj (1, 1) {mustBeA(obj, 'sensingObjective')}; + end + + % Set random objective position + mu = domain.minCorner; + while domain.distance(mu) < protectedRange + mu = domain.random(); + end + mu = mu(1:2); + + % Set random distribution parameters + sig = [2 + rand * 2, 1; 1, 2 + rand * 2]; + + % Set up random bivariate normal distribution function + objectiveFunction = @(x, y) mvnpdf([x(:), y(:)], mu, sig); + + % Regular initialization + obj = obj.initialize(objectiveFunction, domain, discretizationStep); +end \ No newline at end of file diff --git a/@sensingObjective/plot.m b/@sensingObjective/plot.m index b9adbc0..9e48eeb 100644 --- a/@sensingObjective/plot.m +++ b/@sensingObjective/plot.m @@ -18,7 +18,6 @@ function f = plot(obj, ind, f) o.HitTest = 'off'; o.PickableParts = 'none'; hold(f.CurrentAxes, "off"); - else hold(f.Children(1).Children(ind(1)), "on"); o = surf(f.Children(1).Children(ind(1)), obj.X, obj.Y, repmat(obj.groundAlt, size(obj.X)), obj.values ./ max(obj.values, [], "all"), 'EdgeColor', 'none'); diff --git a/@sensingObjective/sensingObjective.m b/@sensingObjective/sensingObjective.m index fbe865a..f24eefa 100644 --- a/@sensingObjective/sensingObjective.m +++ b/@sensingObjective/sensingObjective.m @@ -12,7 +12,8 @@ classdef sensingObjective end methods (Access = public) - [obj] = initialize(obj, objectiveFunction, footprint, groundAlt, discretizationStep); + [obj] = initialize(obj, objectiveFunction, domain, discretizationStep); + [obj] = initializeRandomMvnpdf(obj, domain, protectedRange, discretizationStep); [f ] = plot(obj, ind, f); end end \ No newline at end of file diff --git a/geometries/@rectangularPrism/initialize.m b/geometries/@rectangularPrism/initialize.m index a09d510..66df445 100644 --- a/geometries/@rectangularPrism/initialize.m +++ b/geometries/@rectangularPrism/initialize.m @@ -1,9 +1,11 @@ -function obj = initialize(obj, bounds, tag, label) +function obj = initialize(obj, bounds, tag, label, objectiveFunction, discretizationStep) arguments (Input) obj (1, 1) {mustBeA(obj, 'rectangularPrism')}; bounds (2, 3) double; tag (1, 1) REGION_TYPE = REGION_TYPE.INVALID; label (1, 1) string = ""; + objectiveFunction (1, 1) function_handle = @(x, y) 1; + discretizationStep (1, 1) double = 1; end arguments (Output) obj (1, 1) {mustBeA(obj, 'rectangularPrism')}; @@ -12,7 +14,7 @@ function obj = initialize(obj, bounds, tag, label) obj.tag = tag; obj.label = label; - %% Define geometry bounds by LL corner and UR corner + % Define geometry bounds by LL corner and UR corner obj.minCorner = bounds(1, 1:3); obj.maxCorner = bounds(2, 1:3); @@ -37,4 +39,9 @@ function obj = initialize(obj, bounds, tag, label) [obj.minCorner(1), obj.maxCorner(2)]; ... [obj.maxCorner(1), obj.minCorner(2)]; ... obj.maxCorner(1:2)]; + + % Instantiate sensingObjective only for DOMAIN-type regions + if tag == REGION_TYPE.DOMAIN + obj.objective = sensingObjective; + end end \ No newline at end of file diff --git a/geometries/@rectangularPrism/initializeRandom.m b/geometries/@rectangularPrism/initializeRandom.m new file mode 100644 index 0000000..b57330f --- /dev/null +++ b/geometries/@rectangularPrism/initializeRandom.m @@ -0,0 +1,18 @@ +function [obj] = initializeRandom(obj, minDimension, tag, label) + arguments (Input) + obj (1, 1) {mustBeA(obj, 'rectangularPrism')}; + minDimension (1, 1) double = 10; + tag (1, 1) REGION_TYPE = REGION_TYPE.INVALID; + label (1, 1) string = ""; + end + arguments (Output) + obj (1, 1) {mustBeA(obj, 'rectangularPrism')}; + end + + % Produce random bounds + L = ceil(minDimension + rand * minDimension); + bounds = [zeros(1, 3); L * ones(1, 3)]; + + % Regular initialization + obj = obj.initialize(bounds, tag, label); +end \ No newline at end of file diff --git a/geometries/@rectangularPrism/rectangularPrism.m b/geometries/@rectangularPrism/rectangularPrism.m index f0928e1..59cc217 100644 --- a/geometries/@rectangularPrism/rectangularPrism.m +++ b/geometries/@rectangularPrism/rectangularPrism.m @@ -21,9 +21,14 @@ classdef rectangularPrism % Plotting lines; end + properties (SetAccess = public, GetAccess = public) + % Sensing objective (for DOMAIN region type only) + objective; + end methods (Access = public) - [obj ] = initialize(obj, bounds, tag, label); + [obj ] = initialize(obj, bounds, tag, label, objectiveFunction, discretizationStep); + [obj ] = initializeRandom(obj, tag, label); [r ] = random(obj); [c ] = contains(obj, pos); [d ] = distance(obj, pos); diff --git a/resources/project/qFRcMsci5bTUnTBevgtb2pITyQU/MwRvCS7aBsmP7sDYhggreAErsO8d.xml b/resources/project/qFRcMsci5bTUnTBevgtb2pITyQU/MwRvCS7aBsmP7sDYhggreAErsO8d.xml new file mode 100644 index 0000000..99772b4 --- /dev/null +++ b/resources/project/qFRcMsci5bTUnTBevgtb2pITyQU/MwRvCS7aBsmP7sDYhggreAErsO8d.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/qFRcMsci5bTUnTBevgtb2pITyQU/MwRvCS7aBsmP7sDYhggreAErsO8p.xml b/resources/project/qFRcMsci5bTUnTBevgtb2pITyQU/MwRvCS7aBsmP7sDYhggreAErsO8p.xml new file mode 100644 index 0000000..1599b93 --- /dev/null +++ b/resources/project/qFRcMsci5bTUnTBevgtb2pITyQU/MwRvCS7aBsmP7sDYhggreAErsO8p.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/project/tBBDcBPWdBTCTMSBG9IGJ_TsICk/qyT5ovOLTSj0PEK6QQG5cW9EeTAd.xml b/resources/project/tBBDcBPWdBTCTMSBG9IGJ_TsICk/qyT5ovOLTSj0PEK6QQG5cW9EeTAd.xml new file mode 100644 index 0000000..99772b4 --- /dev/null +++ b/resources/project/tBBDcBPWdBTCTMSBG9IGJ_TsICk/qyT5ovOLTSj0PEK6QQG5cW9EeTAd.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/resources/project/tBBDcBPWdBTCTMSBG9IGJ_TsICk/qyT5ovOLTSj0PEK6QQG5cW9EeTAp.xml b/resources/project/tBBDcBPWdBTCTMSBG9IGJ_TsICk/qyT5ovOLTSj0PEK6QQG5cW9EeTAp.xml new file mode 100644 index 0000000..3727ae7 --- /dev/null +++ b/resources/project/tBBDcBPWdBTCTMSBG9IGJ_TsICk/qyT5ovOLTSj0PEK6QQG5cW9EeTAp.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/test/test_miSim.m b/test/test_miSim.m index 5952af9..fd26fe2 100644 --- a/test/test_miSim.m +++ b/test/test_miSim.m @@ -2,12 +2,15 @@ classdef test_miSim < matlab.unittest.TestCase properties (Access = private) testClass = miSim; - % Domain - domain = rectangularPrism; % domain geometry + % Sim maxIter = 250; timestep = 0.05 partitoningFreq = 5; + % Domain + domain = rectangularPrism; % domain geometry + minDimension = 10; + % Obstacles minNumObstacles = 1; % Minimum number of obstacles to be randomly generated maxNumObstacles = 3; % Maximum number of obstacles to be randomly generated @@ -16,7 +19,7 @@ classdef test_miSim < matlab.unittest.TestCase obstacles = cell(1, 0); % Objective - objectiveDiscretizationStep = 0.01; % Step at which the objective function is solved in X and Y space + discretizationStep = 0.01; % Step at which the objective function is solved in X and Y space protectedRange = 1; % Minimum distance between the sensing objective and the edge of the domain objective = sensingObjective; @@ -39,26 +42,10 @@ classdef test_miSim < matlab.unittest.TestCase methods (TestMethodSetup) % Generate a random domain function tc = setDomain(tc) - % random integer-sized cube domain ranging from [0, 5 -> 25] - % in all dimensions - L = ceil(5 + rand * 10 + rand * 10); - tc.domain = tc.domain.initialize([zeros(1, 3); L * ones(1, 3)], REGION_TYPE.DOMAIN, "Domain"); - end - % Generate a random sensing objective within that domain - function tc = setSensingObjective(tc) - % Using a bivariate normal distribution - % Set peak position (mean) - mu = tc.domain.minCorner; - while tc.domain.distance(mu) < tc.protectedRange - mu = tc.domain.random(); - end - mu(3) = 0; - - % Set standard deviations of bivariate distribution - sig = [2 + rand * 2, 1; 1, 2 + rand * 2]; - - % Define objective - tc.objective = tc.objective.initialize(@(x, y) mvnpdf([x(:), y(:)], mu(1:2), sig), tc.domain.footprint, tc.domain.minCorner(3), tc.objectiveDiscretizationStep); + % random integer-dimensioned cubic domain + tc.domain = tc.domain.initializeRandom(tc.minDimension, REGION_TYPE.DOMAIN, "Domain"); + % Random bivariate normal PDF objective + tc.domain.objective = tc.domain.objective.initializeRandomMvnpdf(tc.domain, tc.protectedRange, tc.discretizationStep); end % Instantiate agents function tc = setAgents(tc) @@ -121,13 +108,13 @@ classdef test_miSim < matlab.unittest.TestCase % Make sure that the obstacles don't cover the sensing % objective - if obstacleCoversObjective(tc.objective, tc.obstacles{ii}) + if obstacleCoversObjective(tc.domain.objective, tc.obstacles{ii}) continue; end % Make sure that the obstacles aren't too close to the % sensing objective - if obstacleCrowdsObjective(tc.objective, tc.obstacles{ii}, tc.protectedRange) + if obstacleCrowdsObjective(tc.domain.objective, tc.obstacles{ii}, tc.protectedRange) continue; end @@ -140,11 +127,11 @@ classdef test_miSim < matlab.unittest.TestCase for ii = 1:size(tc.agents, 1) initInvalid = true; while initInvalid - candidatePos = [tc.objective.groundPos, 0]; + candidatePos = [tc.domain.objective.groundPos, 0]; % Generate a random position for the agent based on % existing agent positions if ii == 1 - while agentsCrowdObjective(tc.objective, candidatePos, mean(tc.domain.dimensions) / 2) + while agentsCrowdObjective(tc.domain.objective, candidatePos, mean(tc.domain.dimensions) / 2) candidatePos = tc.domain.random(); end else @@ -159,7 +146,7 @@ classdef test_miSim < matlab.unittest.TestCase % Make sure that the candidate position does not crowd % the sensing objective and create boring scenarios - if agentsCrowdObjective(tc.objective, candidatePos, mean(tc.domain.dimensions) / 2) + if agentsCrowdObjective(tc.domain.objective, candidatePos, mean(tc.domain.dimensions) / 2) continue; end @@ -243,7 +230,7 @@ classdef test_miSim < matlab.unittest.TestCase end % Initialize the simulation - [tc.testClass, f] = tc.testClass.initialize(tc.domain, tc.objective, tc.agents, tc.timestep, tc.partitoningFreq, tc.maxIter, tc.obstacles); + [tc.testClass, f] = tc.testClass.initialize(tc.domain, tc.domain.objective, tc.agents, tc.timestep, tc.partitoningFreq, tc.maxIter, tc.obstacles); end function misim_run(tc) % randomly create obstacles @@ -289,13 +276,13 @@ classdef test_miSim < matlab.unittest.TestCase % Make sure that the obstacles don't cover the sensing % objective - if obstacleCoversObjective(tc.objective, tc.obstacles{ii}) + if obstacleCoversObjective(tc.domain.objective, tc.obstacles{ii}) continue; end % Make sure that the obstacles aren't too close to the % sensing objective - if obstacleCrowdsObjective(tc.objective, tc.obstacles{ii}, tc.protectedRange) + if obstacleCrowdsObjective(tc.domain.objective, tc.obstacles{ii}, tc.protectedRange) continue; end @@ -308,11 +295,11 @@ classdef test_miSim < matlab.unittest.TestCase for ii = 1:size(tc.agents, 1) initInvalid = true; while initInvalid - candidatePos = [tc.objective.groundPos, 0]; + candidatePos = [tc.domain.objective.groundPos, 0]; % Generate a random position for the agent based on % existing agent positions if ii == 1 - while agentsCrowdObjective(tc.objective, candidatePos, mean(tc.domain.dimensions) / 2) + while agentsCrowdObjective(tc.domain.objective, candidatePos, mean(tc.domain.dimensions) / 2) candidatePos = tc.domain.random(); end else @@ -327,7 +314,7 @@ classdef test_miSim < matlab.unittest.TestCase % Make sure that the candidate position does not crowd % the sensing objective and create boring scenarios - if agentsCrowdObjective(tc.objective, candidatePos, mean(tc.domain.dimensions) / 2) + if agentsCrowdObjective(tc.domain.objective, candidatePos, mean(tc.domain.dimensions) / 2) continue; end @@ -411,7 +398,7 @@ classdef test_miSim < matlab.unittest.TestCase end % Initialize the simulation - [tc.testClass, f] = tc.testClass.initialize(tc.domain, tc.objective, tc.agents, tc.timestep, tc.partitoningFreq, tc.maxIter, tc.obstacles); + [tc.testClass, f] = tc.testClass.initialize(tc.domain, tc.domain.objective, tc.agents, tc.timestep, tc.partitoningFreq, tc.maxIter, tc.obstacles); % Run simulation loop [tc.testClass, f] = tc.testClass.run(f); @@ -424,7 +411,7 @@ classdef test_miSim < matlab.unittest.TestCase tc.domain = tc.domain.initialize([zeros(1, 3); 10 * ones(1, 3)], REGION_TYPE.DOMAIN, "Domain"); % make basic sensing objective - tc.objective = tc.objective.initialize(@(x, y) mvnpdf([x(:), y(:)], tc.domain.center(1:2), eye(2)), tc.domain.footprint, tc.domain.minCorner(3), tc.objectiveDiscretizationStep); + tc.domain.objective = tc.domain.objective.initialize(@(x, y) mvnpdf([x(:), y(:)], tc.domain.center(1:2), eye(2)), tc.domain.footprint, tc.domain.minCorner(3), tc.discretizationStep); % Initialize agent collision geometry geometry1 = rectangularPrism; @@ -442,7 +429,7 @@ classdef test_miSim < matlab.unittest.TestCase tc.agents{2} = tc.agents{2}.initialize(tc.domain.center - [d, 0, 0], zeros(1,3), 0, 0, geometry2, sensor, @gradientAscent, 3*d, 2, sprintf("Agent %d", 2)); % Initialize the simulation - [tc.testClass, f] = tc.testClass.initialize(tc.domain, tc.objective, tc.agents, tc.timestep, tc.partitoningFreq, tc.maxIter); + [tc.testClass, f] = tc.testClass.initialize(tc.domain, tc.domain.objective, tc.agents, tc.timestep, tc.partitoningFreq, tc.maxIter); end end end \ No newline at end of file