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 all 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 @@ -33,6 +33,9 @@
import org.eclipse.mosaic.lib.util.scheduling.EventProcessor;
import org.eclipse.mosaic.rti.TIME;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

Expand Down Expand Up @@ -166,6 +169,32 @@ GeoPoint getTargetPosition() {
return homePosition;
}

@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (obj.getClass() != getClass()) {
return false;
}
DriveBackEvent event = (DriveBackEvent) obj;
return new EqualsBuilder()
.appendSuper(super.equals(obj))
.append(this.currentPosition, event.currentPosition)
.append(this.homePosition, event.homePosition)
.isEquals();
}

@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(currentPosition)
.append(homePosition)
.toHashCode();
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
package org.eclipse.mosaic.app.tutorial.vehicle;

import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.SimplePerceptionConfiguration;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.errormodels.DistanceModifier;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.errormodels.PositionErrorModifier;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.errormodels.SimpleOcclusionModifier;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.errormodels.DistanceFilter;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.errormodels.PositionModifier;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.errormodels.SimpleOcclusion;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.index.objects.VehicleObject;
import org.eclipse.mosaic.fed.application.app.AbstractApplication;
import org.eclipse.mosaic.fed.application.app.api.VehicleApplication;
Expand Down Expand Up @@ -66,15 +66,15 @@ public void onStartup() {

private void enablePerceptionModule() {
// filter to emulate occlusion
SimpleOcclusionModifier simpleOcclusionModifier = new SimpleOcclusionModifier(3, 5);
SimpleOcclusion simpleOcclusion = new SimpleOcclusion(3, 5);
// filter to reduce perception probability based on distance to ego vehicle
DistanceModifier distanceModifier = new DistanceModifier(getRandom(), 0.0);
DistanceFilter distanceFilter = new DistanceFilter(getRandom(), 0.0);
// filter adding noise to longitudinal and lateral
PositionErrorModifier positionErrorModifier = new PositionErrorModifier(getRandom());
PositionModifier positionModifier = new PositionModifier(getRandom());

SimplePerceptionConfiguration perceptionModuleConfiguration =
new SimplePerceptionConfiguration.Builder(VIEWING_ANGLE, VIEWING_RANGE)
.addModifiers(simpleOcclusionModifier, distanceModifier, positionErrorModifier)
.addModifiers(simpleOcclusion, distanceFilter, positionModifier)
.build();
getOs().getPerceptionModule().enable(perceptionModuleConfiguration);
}
Expand Down
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 @@ -226,68 +226,47 @@ static class MonitoringTrafficObjectIndexProvider extends TrafficObjectIndex {
@Override
public List<VehicleObject> getVehiclesInRange(PerceptionModel searchRange) {
try (PerformanceMonitor.Measurement m = monitor.start("search-vehicle")) {
m.setProperties(getNumberOfVehicles(), SimulationKernel.SimulationKernel.getCurrentSimulationTime())
.restart();
m.setProperties(getNumberOfVehicles(), SimulationKernel.SimulationKernel.getCurrentSimulationTime()).restart();
return super.getVehiclesInRange(searchRange);
}
}

@Override
public void removeVehicles(Iterable<String> vehiclesToRemove) {
try (PerformanceMonitor.Measurement m = monitor.start("remove-vehicle")) {
m.setProperties(getNumberOfVehicles(), SimulationKernel.SimulationKernel.getCurrentSimulationTime())
.restart();
m.setProperties(getNumberOfVehicles(), SimulationKernel.SimulationKernel.getCurrentSimulationTime()).restart();
super.removeVehicles(vehiclesToRemove);
}
}

@Override
public void updateVehicles(Iterable<VehicleData> vehiclesToUpdate) {
try (PerformanceMonitor.Measurement m = monitor.start("update-vehicle")) {
m.setProperties(getNumberOfVehicles(), SimulationKernel.SimulationKernel.getCurrentSimulationTime())
.restart();
m.setProperties(getNumberOfVehicles(), SimulationKernel.SimulationKernel.getCurrentSimulationTime()).restart();
super.updateVehicles(vehiclesToUpdate);
}
}

@Override
public int getNumberOfVehicles() {
return super.getNumberOfVehicles();
}

@Override
public List<TrafficLightObject> getTrafficLightsInRange(PerceptionModel perceptionModel) {
try (PerformanceMonitor.Measurement m = monitor.start("search-traffic-light")) {
m.setProperties(getNumberOfTrafficLights(), SimulationKernel.SimulationKernel.getCurrentSimulationTime())
.restart();
m.setProperties(SimulationKernel.SimulationKernel.getCurrentSimulationTime()).restart();
kschrab marked this conversation as resolved.
Show resolved Hide resolved
return super.getTrafficLightsInRange(perceptionModel);
}
}

@Override
public void addTrafficLightGroup(TrafficLightGroup trafficLightGroup) {
super.addTrafficLightGroup(trafficLightGroup);
}

@Override
public void updateTrafficLights(Map<String, TrafficLightGroupInfo> trafficLightsToUpdate) {
try (PerformanceMonitor.Measurement m = monitor.start("update-traffic-light")) {
m.setProperties(getNumberOfTrafficLights(), SimulationKernel.SimulationKernel.getCurrentSimulationTime())
.restart();
m.setProperties(SimulationKernel.SimulationKernel.getCurrentSimulationTime()).restart();
super.updateTrafficLights(trafficLightsToUpdate);
}
}

@Override
public int getNumberOfTrafficLights() {
return super.getNumberOfTrafficLights();
}

@Override
public Collection<Edge<Vector3d>> getSurroundingWalls(PerceptionModel perceptionModel) {
try (PerformanceMonitor.Measurement m = monitor.start("search-walls")) {
m.setProperties(getNumberOfTrafficLights(), SimulationKernel.SimulationKernel.getCurrentSimulationTime())
.restart();
m.setProperties(SimulationKernel.SimulationKernel.getCurrentSimulationTime()).restart();
return super.getSurroundingWalls(perceptionModel);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*
* 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.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 BoundingBoxOcclusion implements PerceptionModifier {

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 BoundingBoxOcclusion}.
* Uses {@link #pointsPerSide} = 2 and {@link #detectionThreshold} = 2 as default values.
*/
public BoundingBoxOcclusion() {
this.pointsPerSide = 2;
this.detectionThreshold = 2;
}

/**
* Constructor for {@link BoundingBoxOcclusion}, validates and sets
* the parameters {@link #pointsPerSide} and {@link #detectionThreshold}.
*
* @param pointsPerSide the number of points that will be evaluated per object side (corners count towards 2 edges)
* @param detectionThreshold how many points have to be visible in order for an object to be treated as detected
*/
public BoundingBoxOcclusion(int pointsPerSide, int detectionThreshold) {
if (pointsPerSide < 2) {
throw new IllegalArgumentException("Need at least 2 points per edge, meaning every corner will be checked for occlusion.");
}
if (detectionThreshold < 1) {
throw new IllegalArgumentException("At least one point has to be checked for occlusion, else no objects will be occluded");
}
if (detectionThreshold > pointsPerSide * 4 - 4) {
throw new IllegalArgumentException("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());
Vector3d egoPosition = owner.getVehicleData().getProjectedPosition().toVector3d();
for (T objectToEvaluate : spatialObjects) {
if (objectToEvaluate instanceof TrafficLightObject) { // Traffic Lights are treated to not be occluded
newObjects.add(objectToEvaluate);
continue;
}
List<Vector3d> pointsToEvaluate = createPointsToEvaluate(objectToEvaluate);
final int requiredVisiblePoints = pointsToEvaluate.size() == 1 ? 1 : detectionThreshold;
int numberOfPointsVisible = 0;
for (Vector3d point : pointsToEvaluate) {
boolean pointVisible = isVisible(egoPosition, point, objectToEvaluate.getId(), occludingObjects);
if (pointVisible) { // increment visible counter
numberOfPointsVisible++;
}
// if the required number of points is visible, we don't need to evaluate more
if (numberOfPointsVisible == requiredVisiblePoints) {
newObjects.add(objectToEvaluate);
break;
}
}
}
return newObjects;
}

/**
* Method to evaluate whether a point is visible by any edge spanned by any other bounding box of any other vehicle.
*
* @param egoPosition position of the ego vehicle
* @param pointToEvaluate the point that should be checked for occlusion
* @param objectId id that the point belongs to (required for points not to be occluded by the same vehicle)
* @param occludingObjects all objects that potentially occlude the vehicle
* @return {@code true} if the point is visible, else {@code false}
*/
private <T extends SpatialObject> boolean isVisible(Vector3d egoPosition, Vector3d pointToEvaluate, String objectId, List<T> occludingObjects) {
for (T occludingObject : occludingObjects) {
if (occludingObject.getId().equals(objectId)) {
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(
egoPosition,
pointToEvaluate, side.a, side.b, intersectionResult
);
if (isOccluded) {
return false;
}
}
}
return true;
}

/**
* 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 @@ -34,7 +34,7 @@
* This modifier can also be configured using {@link #offset}, which allows adjusting the
* stochastic component to allow for more or less perceptions.
*/
public class DistanceModifier implements PerceptionModifier {
public class DistanceFilter implements PerceptionModifier {

private final RandomNumberGenerator rng;

Expand All @@ -44,15 +44,15 @@ public class DistanceModifier implements PerceptionModifier {
*/
private final double offset;

public DistanceModifier(RandomNumberGenerator rng, double offset) {
public DistanceFilter(RandomNumberGenerator rng, double offset) {
Validate.isTrue(offset >= -1 && offset <= 1, "The offset has to be between -1 and 1.");
this.rng = rng;
this.offset = 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
Loading