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

feat(perception): Use 2D bounding boxes for Occlusion Modifiers #343

Merged
merged 18 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
9736332
fix(perception): some fixes in perception model
schwepmo Aug 17, 2023
3bad5e6
feat(perception): initial, non-functional bounding box occlusion
schwepmo Aug 17, 2023
ce70fc2
feat(perception): added bounding box occlusion modifier (needs to be …
schwepmo Aug 17, 2023
96dd40e
feat(perception): added bounding box attribute to SpatialObject and p…
schwepmo Aug 18, 2023
0a4e4e4
feat(perception): added additional check in BoundingBoxOcclusionModif…
schwepmo Aug 21, 2023
3727cca
feat(perception): extended bounding box occlusion test
schwepmo Aug 21, 2023
1a2beb2
fix(perception): working on PR-review
schwepmo Aug 30, 2023
66ef4ba
Merge branch 'main' of github.com:eclipse/mosaic into 630-bounding-bo…
schwepmo Aug 30, 2023
1146d2e
fix(perception): working on PR-review
schwepmo Aug 30, 2023
b5493fa
fix(perception): working on PR-review
schwepmo Aug 30, 2023
c602258
cleanup(mapping): fixed check style warnings
schwepmo Sep 4, 2023
ab4e511
cleanup(apps): fixing spotbugs warning in example app
schwepmo Sep 4, 2023
ee82c00
cleanup(perception): fixing spotbugs warning in perception objects
schwepmo Sep 4, 2023
188b531
cleanup(perception): trying to fix spotbugs warning in perception obj…
schwepmo Sep 4, 2023
b4e93a1
cleanup(perception): trying to fix spotbugs warning in perception obj…
schwepmo Sep 4, 2023
a39a6e7
cleanup(example-apps): fixing spotbugs warning example app
schwepmo Sep 4, 2023
9c5ce92
cleanup(perception): tyring to spotbugs warnings in perception
schwepmo Sep 4, 2023
af515e4
cleanup(perception): reverted additional variable
schwepmo Sep 11, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public List<SpatialObject> getPerceivedObjects() {

private <T extends SpatialObject<T>> List<T> applyPerceptionModifiers(List<T> objectsInRange) {
List<T> filteredList = new ArrayList<>(objectsInRange);
// create copy of all perceived objects to avoid interference with modifiers of other perception modules.
// create a copy of all perceived objects to avoid interference with modifiers of other perception modules.
filteredList.replaceAll(T::copy);
for (PerceptionModifier perceptionModifier : configuration.getPerceptionModifiers()) {
filteredList = perceptionModifier.apply(owner, filteredList); // apply filters in sequence
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ List<VehicleObject> getVehiclesInRange() {
.setSpeed(v.getSpeed())
.setHeading(v.getHeading())
.setDimensions(v.getLength(), v.getWidth(), v.getHeight())
.resetBoundingBox()
).collect(Collectors.toList());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* Copyright (c) 2023 Fraunhofer FOKUS and others. All rights reserved.
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contact: [email protected]
*/

package org.eclipse.mosaic.fed.application.ambassador.simulation.perception.errormodels;

import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.PerceptionModuleOwner;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.index.objects.SpatialObject;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.index.objects.SpatialObjectBoundingBox;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.index.objects.TrafficLightObject;
import org.eclipse.mosaic.lib.math.MathUtils;
import org.eclipse.mosaic.lib.math.Vector3d;
import org.eclipse.mosaic.lib.math.VectorUtils;
import org.eclipse.mosaic.lib.spatial.Edge;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class BoundingBoxOcclusionModifier implements PerceptionModifier {
schwepmo marked this conversation as resolved.
Show resolved Hide resolved

private final Vector3d intersectionResult = new Vector3d();
/**
* This defines how many equidistant points shall be evaluated per edge of a vehicle.
* Note generally for the front and rear edge this will result in a higher resolution compared to the sides of the vehicle.
* Default: 2
*/
private final int pointsPerSide;

/**
* Threshold that defines how many of the points defined through {@link #pointsPerSide} need to be visible in order for a
* object to be treated as detected.
* Default: 2
*/
private final int detectionThreshold;

/**
* Default constructor for the {@link BoundingBoxOcclusionModifier}.
schwepmo marked this conversation as resolved.
Show resolved Hide resolved
*/
public BoundingBoxOcclusionModifier() {
this.pointsPerSide = 2;
this.detectionThreshold = 2;
}

/**
* Constructor for {@link BoundingBoxOcclusionModifier}, validates and sets
* the parameters {@link #pointsPerSide} and {@link #detectionThreshold}.
*
* @param pointsPerSide the amount of points that will be evaluated per object (corners count towards 2 edges)
schwepmo marked this conversation as resolved.
Show resolved Hide resolved
* @param detectionThreshold how many points have to be visible in order for an object to be treated as detected
*/
public BoundingBoxOcclusionModifier(int pointsPerSide, int detectionThreshold) {
if (pointsPerSide < 2) {
throw new RuntimeException("Need at least 2 points per edge, meaning every corner will be checked for occlusion.");
schwepmo marked this conversation as resolved.
Show resolved Hide resolved
}
if (detectionThreshold < 1) {
throw new RuntimeException("At least one point has to be checked for occlusion, else no objects will be occluded");
}
if (detectionThreshold > pointsPerSide * 4 - 4) {
throw new RuntimeException("The detection threshold exceeds the number of points evaluated per object");
}
this.pointsPerSide = pointsPerSide;
this.detectionThreshold = detectionThreshold;
}

@Override
public <T extends SpatialObject> List<T> apply(PerceptionModuleOwner owner, List<T> spatialObjects) {
List<T> newObjects = new ArrayList<>();
// the ego object cannot occlude vision
List<T> occludingObjects = spatialObjects.stream()
.filter(object -> !object.getId().equals(owner.getId()))
.collect(Collectors.toList());
for (T spatialObject : spatialObjects) {
if (spatialObject instanceof TrafficLightObject) { // Traffic Lights are treated to not be occluded
newObjects.add(spatialObject);
continue;
}
List<Vector3d> pointsToEvaluate = createPointsToEvaluate(spatialObject);
final int requiredVisiblePoints = pointsToEvaluate.size() == 1 ? 1 : detectionThreshold;
int numberOfPointsVisible = 0;
for (Vector3d point : pointsToEvaluate) {
boolean pointOccluded = false;
boundingBoxLoop:
schwepmo marked this conversation as resolved.
Show resolved Hide resolved
for (T occludingObject : occludingObjects) {
if (occludingObject.getId().equals(spatialObject.getId())) {
continue; // cannot be occluded by itself
}
SpatialObjectBoundingBox boundingBox = occludingObject.getBoundingBox();
// SpatialObjects with PointBoundingBoxes won't occlude anything, as they have no edges defined
for (Edge<Vector3d> side : boundingBox.getAllEdges()) {
boolean isOccluded = VectorUtils.computeXZEdgeIntersectionPoint(
owner.getVehicleData().getProjectedPosition().toVector3d(),
point, side.a, side.b, intersectionResult
);
if (isOccluded) {
pointOccluded = true;
break boundingBoxLoop;
}
}
}
if (!pointOccluded) {
numberOfPointsVisible++;
}
if (numberOfPointsVisible == requiredVisiblePoints) {
newObjects.add(spatialObject);
break;
}
}
}
return newObjects;
}

/**
* Creates a list of all points that shall be tested for occlusion. If {@link #pointsPerSide} is set to a value larger than 2,
* each side will have additional equidistant points added.
* Example for {@code pointsPerSide = 3} (x's are the corners which will be evaluated anyway, o's are the added points):
* <pre>
* x-----o-----x
* | |
* | |
* o o
* | |
* | |
* x-----o-----y
* </pre>
* @param spatialObject a {@link SpatialObject} for which the occlusion should be evaluated
*/
private <T extends SpatialObject> List<Vector3d> createPointsToEvaluate(T spatialObject) {
List<Vector3d> pointsToEvaluate = new ArrayList<>();
SpatialObjectBoundingBox boundingBox = spatialObject.getBoundingBox();
// if object has edges and more than 2 points per side are to be evaluated, calculate points that have to be evaluated
if (pointsPerSide > 2 && !boundingBox.getAllEdges().isEmpty()) {
for (Edge<Vector3d> edge : boundingBox.getAllEdges()) {
Vector3d start = edge.a;
if (pointNotPresent(pointsToEvaluate, start)) {
pointsToEvaluate.add(start);
}
Vector3d end = edge.b;
if (pointNotPresent(pointsToEvaluate, end)) {
pointsToEvaluate.add(end);
}
for (int i = 1; i < pointsPerSide - 1; i++) {
double ratio = (double) i / (pointsPerSide + 1);
double xNew = start.x + ratio * (end.x - start.x);
double zNew = start.z + ratio * (end.z - start.z);
Vector3d newPoint = new Vector3d(xNew, 0, zNew);
if (pointNotPresent(pointsToEvaluate, newPoint)) {
pointsToEvaluate.add(newPoint);
}
}
}
} else { // else just add all corners
pointsToEvaluate.addAll(boundingBox.getAllCorners());
}
return pointsToEvaluate;
}

private boolean pointNotPresent(List<Vector3d> points, Vector3d newPoint) {
return points.stream().noneMatch(vector3d -> vector3d.isFuzzyEqual(newPoint));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public DistanceModifier(RandomNumberGenerator rng, double offset) {

@Override
public <T extends SpatialObject> List<T> apply(PerceptionModuleOwner owner, List<T> spatialObjects) {
if (spatialObjects.size() == 0) {
if (spatialObjects.isEmpty()) {
return spatialObjects;
}
Vector3d ownerPosition = owner.getVehicleData().getProjectedPosition().toVector3d();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.PerceptionModuleOwner;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.index.objects.SpatialObject;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.index.objects.VehicleObject;
import org.eclipse.mosaic.lib.math.RandomNumberGenerator;
import org.eclipse.mosaic.lib.math.Vector3d;
import org.eclipse.mosaic.lib.math.VectorUtils;
Expand Down Expand Up @@ -85,7 +86,7 @@ public <T extends SpatialObject> List<T> apply(PerceptionModuleOwner owner, List
double angleToNorth = ownerDirection.angle(VectorUtils.NORTH);
spatialObjects.forEach(
spatialObject -> {
Vector3d relativePosition = getVectorRelativeTo(ownerPosition, spatialObject.getPosition()); // get position relative to owner
Vector3d relativePosition = getVectorRelativeTo(ownerPosition, spatialObject.getPosition()); // pos relative to owner
Vector3d adjustedVector = new Vector3d(relativePosition);
adjustedVector.rotate(-angleToNorth, VectorUtils.UP); // rotate vector according to orientation
// add lateral and longitudinal errors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public SimpleOcclusionModifier(double minDetectionAngle, double maxDetectionAngl

@Override
public <T extends SpatialObject> List<T> apply(PerceptionModuleOwner owner, List<T> spatialObjects) {
if (spatialObjects.size() == 0) {
if (spatialObjects.isEmpty()) {
return spatialObjects;
}
Vector3d ownerPosition = owner.getVehicleData().getProjectedPosition().toVector3d();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,37 +33,55 @@
*/
public class WallOcclusionModifier implements PerceptionModifier {

private final int requiredVisibleVehicleCorners;
private final Vector3d intersectionResult = new Vector3d();

public WallOcclusionModifier() {
requiredVisibleVehicleCorners = 2;
}

public WallOcclusionModifier(int requiredVisibleVehicleCorners) {
this.requiredVisibleVehicleCorners = requiredVisibleVehicleCorners;
}

@Override
public <T extends SpatialObject> List<T> apply(PerceptionModuleOwner owner, List<T> spatialObjects) {
if (spatialObjects.size() == 0) {
if (spatialObjects.isEmpty()) {
return spatialObjects;
}

final Collection<Edge<Vector3d>> walls = owner.getPerceptionModule().getSurroundingWalls();
if (walls.isEmpty()) {
return spatialObjects;
}
final Vector3d ownerPosition = owner.getVehicleData().getProjectedPosition().toVector3d();
final Vector3d otherPosition = new Vector3d();

final List<T> result = new ArrayList<>();

vehicleLoop:
for (T spatialObject : spatialObjects) {
spatialObject.getProjectedPosition().toVector3d(otherPosition);

for (Edge<Vector3d> wall : walls) {
boolean isHidden =
VectorUtils.computeXZEdgeIntersectionPoint(ownerPosition, otherPosition, wall.a, wall.b, intersectionResult);
if (isHidden) {
continue vehicleLoop;
List<Vector3d> pointsToEvaluate = spatialObject.getBoundingBox().getAllCorners();
// we say that at least half of the corners have to be visible rounding up for odd numbers
final int requiredVisiblePoints = (int) Math.ceil((double) pointsToEvaluate.size() / 2);
int numberOfPointsVisible = 0;
for (Vector3d point : pointsToEvaluate) {
boolean pointOccluded = false;
for (Edge<Vector3d> wall : walls) {
// SpatialObjects with PointBoundingBoxes won't occlude anything, as they have no edges defined
boolean isOccluded = VectorUtils.computeXZEdgeIntersectionPoint(
owner.getVehicleData().getProjectedPosition().toVector3d(),
point, wall.a, wall.b, intersectionResult
);
if (isOccluded) {
pointOccluded = true;
break;
}
}
if (!pointOccluded) {
numberOfPointsVisible++;
}
if (numberOfPointsVisible == requiredVisiblePoints) {
result.add(spatialObject);
break;
}
}
result.add(spatialObject);
}

return result;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2023 Fraunhofer FOKUS and others. All rights reserved.
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contact: [email protected]
*/

package org.eclipse.mosaic.fed.application.ambassador.simulation.perception.index.objects;

import org.eclipse.mosaic.lib.math.Vector3d;
import org.eclipse.mosaic.lib.spatial.Edge;

import java.util.ArrayList;
import java.util.List;

/**
* This bounding box represents
*/
public class PointBoundingBox implements SpatialObjectBoundingBox {

/**
* A one element list, defining the position of a {@link SpatialObject} as the only corner.
*/
private final List<Vector3d> allCorners = new ArrayList<>();
/**
* An empty list as a no bounding box is spanned by a single point.
*/
private final List<Edge<Vector3d>> allEdges = new ArrayList<>();

public PointBoundingBox(Vector3d position) {
allCorners.add(position);
schwepmo marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
public List<Vector3d> getAllCorners() {
return allCorners;
}

@Override
public List<Edge<Vector3d>> getAllEdges() {
return allEdges;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@ public abstract class SpatialObject<T extends SpatialObject<T>> extends Vector3d

private final String id;

final MutableCartesianPoint cartesianPosition = new MutableCartesianPoint();
protected final MutableCartesianPoint cartesianPosition = new MutableCartesianPoint();

public SpatialObject(String id) {
this.id = id;
}

public abstract T setPosition(CartesianPoint position);

public abstract SpatialObjectBoundingBox getBoundingBox();
schwepmo marked this conversation as resolved.
Show resolved Hide resolved

/**
* Sets the position of the {@link SpatialObject}.
*
Expand Down
Loading