Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

45 update position generators and re implement collision avoidance scenes #46

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/common/robot/robot.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ export default class Robot {
}

reached(point) {
const ret = this.getDistanceTo(point) <= this.radius / 50;
const ret = this.getDistanceTo(point) <= this.radius / 5;
return ret;
}

Expand Down
6 changes: 3 additions & 3 deletions src/common/scene/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,8 @@ export default class Scene {
return d3.range(numOfRobots)
.map((i) => new Robot(
i,
this.getPos(),
this.getPos(),
this.getPos(radius),
this.getPos(radius),
controllers,
sensors,
actuators,
Expand All @@ -329,7 +329,7 @@ export default class Scene {
...d3.range(puckGroup.count)
.map((i) => new Puck(
i + id,
this.getPos(),
this.getPos(puckGroup.radius),
puckGroup.radius,
puckGroup.goal,
puckGroup.goalRadius,
Expand Down
39 changes: 39 additions & 0 deletions src/common/utils/positionsGenerators/circularPositionsGenerator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export default function getCircularPositionsGenerator(posNum, radius, envWidth, envHeight) {
const circleRadius = (Math.min(envWidth, envHeight) * 20) / 42;
const resolution = (Math.PI * 2) / posNum;
const envCenter = { x: envWidth / 2, y: envHeight / 2 };

if (circleRadius * resolution < radius * 4) {
throw new Error('Invalid inputs, number and size of robots are too high for this environment size!');
}

const positions = [];
const start = Math.random() * Math.PI * 2;
let i = start;
while (i < start + Math.PI * 2) {
const newX = envCenter.x + circleRadius * Math.cos(i);
const newY = envCenter.y + circleRadius * Math.sin(i);
const newGoalX = envCenter.x - circleRadius * Math.cos(i);
const newGoalY = envCenter.y - circleRadius * Math.sin(i);
const newPos = { x: newX, y: newY };
const newGoalPos = { x: newGoalX, y: newGoalY };

positions.push(newPos);
positions.push(newGoalPos);

i += resolution + (Math.random() * resolution) / 100 - resolution / 50;
}

if (positions.length < posNum * 2) {
throw new Error('Invalid inputs, number and size of robots are too high for this environment size!');
}

const getPos = () => {
if (positions.length === 0) {
throw new Error('No positions available!');
}
return positions.pop();
};

return getPos;
}
4 changes: 3 additions & 1 deletion src/common/utils/positionsGenerators/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import randomCollisionFree from './randomPositionsGenerator';
import circularPositionsGenerator from './circularPositionsGenerator';

export default {
randomCollisionFree
randomCollisionFree,
circularPositionsGenerator
};
88 changes: 38 additions & 50 deletions src/common/utils/positionsGenerators/randomPositionsGenerator.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,47 @@
// Module to generate random initial positions for robots and pucks
// TODO: replace with a js generator

import { getDistance } from '../geometry';

const positions = [];

const getPos = () => {
if (positions.length === 0) {
throw new Error('No positions available!');
}
return positions.pop();
};

export default function getRandCollFreePosGenerator(
numOfPos, radius, envWidth, envHeight, staticObjects
) {
const resolution = (radius * 2.1);
const xCount = envWidth / resolution;
const yCount = envHeight / resolution;
const positionsCount = parseInt(numOfPos, 10);

/*
if (xCount * yCount < positionsCount * 4) {
throw new Error('Invalid inputs, number and size of robots and pucks are too high for this environment size!');
}
*/

let i = 0;
while (positions.length < positionsCount * 3 && i < positionsCount * 100) {
const newX = Math.max(
radius * 2,
Math.min(envWidth - radius * 2, Math.floor(Math.random() * xCount) * resolution)
);
const newY = Math.max(
radius * 2,
Math.min(envHeight - radius * 2, Math.floor(Math.random() * yCount) * resolution)
);
export default function getRandCollFreePosGenerator(posNum, radius, envWidth, envHeight, staticObjects = []) {
const positionsAndRadii = [];

const generatePos = (r) => {
const xCount = envWidth / r;
const yCount = envHeight / r;
const minX = r * 2;
const maxX = envWidth - r * 2;
const minY = r * 2;
const maxY = envHeight - r * 2;

const newX = Math.max(minX, Math.min(maxX, Math.floor(Math.random() * xCount) * r));
const newY = Math.max(minY, Math.min(maxY, Math.floor(Math.random() * yCount) * r));
const newPos = { x: newX, y: newY };
const doesNotCollideWithRobots = positions
.findIndex((x) => getDistance(x, newPos) < radius * 2.2) === -1;
const doesNotCollideWithObstacles = staticObjects
.reduce((acc, cur) => !cur.containsPoint(newPos)
&& cur.getDistanceToBorder(newPos) > radius && acc, true);

if (doesNotCollideWithRobots && doesNotCollideWithObstacles) {
positions.push(newPos);

const doesNotCollideWithOtherPositions = positionsAndRadii
.findIndex(([p, pr]) => getDistance(p, newPos) < (r + pr) * 1.1) === -1;

const doesNotCollideWithObstacles = staticObjects.reduce((acc, cur) => !cur.containsPoint(newPos)
&& cur.getDistanceToBorder(newPos) > r && acc, true);

if (doesNotCollideWithOtherPositions && doesNotCollideWithObstacles) {
return newPos;
}
i += 1;
}

/*
if (positions.length < positionsCount * 2) {
throw new Error('Invalid inputs, number and size of robots are too high for this environment!');
}
*/

return null;
};

const getPos = (r) => {
for (let tries = 0; tries < 100000; tries += 1) {
const newPos = generatePos(r);

if (newPos) {
positionsAndRadii.push([newPos, r]);
return newPos;
}
}

throw new Error('No collision-free positions available!');
};

return getPos;
}
113 changes: 113 additions & 0 deletions src/scenes/CollisionAvoidance/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import {
CoreActuators,
CoreSensors,
ExtraSensors,
CorePositionsGenerators,
CorePerformanceTrakers,
CoreControllers
} from '@common';

import SceneRenderables from '@common/scene/renderables';
import RobotRenderables from '@common/robot/renderables';
import PuckRenderables from '@common/puck/renderables';

const renderables = [
{ module: 'Scene', elements: SceneRenderables },
{ module: 'Puck', elements: PuckRenderables },
{ module: 'Robot', elements: RobotRenderables }
];

const simConfig = {
env: {
width: 800,
height: 500,
renderSkip: 1
},
robots: {
count: 25,
radius: 7,
params: {
velocityScale: 10
},
controllers: {
waypoint: CoreControllers.waypoint.bvcWaypointController,
// velocity: CoreControllers.velocity.omniDirVelocityController
velocity: {
controller: CoreControllers.velocity.diffVelocityController,
params: { angularVelocityScale: 0.01 }
},
supportsUserDefinedControllers: false
},
sensors: [...Object.values(CoreSensors), ...Object.values(ExtraSensors)],
actuators: Object.values(CoreActuators),
useVoronoiDiagram: true
},
pucks: {
groups: [],
useGlobalPuckMaps: false
},
objects: [],
positionsGenerator: CorePositionsGenerators.circularPositionsGenerator,
renderables
};

// TODO: add other waypoint controller to benchmark
const benchmarkConfig = {
simConfigs: [
{
name: '5 Robots',
simConfig: {
env: {
renderSkip: 50
},
robots: {
count: 5,
params: {
velocityScale: 50
}
}
}
},
{
name: '20 Robots',
simConfig: {
env: {
renderSkip: 50
},
robots: {
params: {
velocityScale: 50
}
}
}
}
],
trackers: [
CorePerformanceTrakers.RobotToGoalDistanceTracker,
CorePerformanceTrakers.PucksOutsideGoalTracker,
CorePerformanceTrakers.MinRobotRobotDistanceTracker
],
maxTimeStep: 50000,
timeStep: 1000
};

const description = {
html: `<p>Object sorting using Buffered Voronoi Cells (BVC). Each robot chooses an intermediate goal location within its BVC. This avoids the possibility of collision or conflict with other robots. Their goal is to incremental shift the pucks towards their respective goal locations.</p>

<p>Rather than pushing pucks directly towards their goals, the robots make use of a goal map for each type of pucks. A goal map specifies the direction a puck should be pushed in order to reach the goal, accounting for obstacles that might be in the way.</p>

<p>
<a href=https://link.springer.com/chapter/10.1007/978-3-031-20176-9_27 target=_blank>
Abdullhak, Mohammed, and Andrew Vardy. "Distributed Sorting in Complex Environments." Swarm Intelligence: 13th International Conference, ANTS 2022, Málaga, Spain, November 2–4, 2022, Proceedings. Cham: Springer International Publishing, 2022.
</a>
</p>
`
};

export default {
title: 'Collision Avoidance',
name: 'collisionAvoidance',
simConfig,
benchmarkConfig,
description
};
3 changes: 2 additions & 1 deletion src/scenes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import voronoiSorting from './VoronoiSorting';
import simpleSorting from './Sorting';
import demo from './Demo';
import labyrinth from './Labyrinth';
import collisionAvoidance from './CollisionAvoidance';

export default {
labyrinth,
Expand All @@ -17,5 +18,5 @@ export default {
fieldManipulation,
simpleSorting,
voronoiSorting,
ExternalEngine
collisionAvoidance
};