Skip to content

Commit e9f8c64

Browse files
Finja Averhausmartingr
andcommitted
Add support for mapping coordinates to a logical position
Add support for determining a vehicle's (logical) position based on the vehicle's reported precise position. Vehicle drivers can now request this via `VehicleProcessModel.positionMappingRequested(Pose)`. Co-authored-by: Martin Grzenia <martin.grzenia@iml.fraunhofer.de> Merged-by: Martin Grzenia <martin.grzenia@iml.fraunhofer.de>
1 parent 7da8f35 commit e9f8c64

File tree

11 files changed

+457
-3
lines changed

11 files changed

+457
-3
lines changed

opentcs-api-base/src/main/java/org/opentcs/drivers/vehicle/VehicleProcessModel.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,20 @@ public void integrationLevelChangeRequested(
668668
);
669669
}
670670

671+
/**
672+
* Notifies observers that the vehicle would like to have its (logical) position
673+
* determined based on a precise position.
674+
*
675+
* @param precisePosition The precise position.
676+
*/
677+
public void positionResolutionRequested(Pose precisePosition) {
678+
getPropertyChangeSupport().firePropertyChange(
679+
Attribute.POSITION_RESOLUTION_REQUESTED.name(),
680+
position,
681+
precisePosition
682+
);
683+
}
684+
671685
/**
672686
* Notifies observers that the vehicle would like to have its current transport order withdrawn.
673687
*
@@ -858,6 +872,11 @@ public enum Attribute {
858872
/**
859873
* Indicates a request to withdraw the vehicles current transport order.
860874
*/
861-
TRANSPORT_ORDER_WITHDRAWAL_REQUESTED;
875+
TRANSPORT_ORDER_WITHDRAWAL_REQUESTED,
876+
/**
877+
* Indicates a request to determine the vehicle's (logical) position based on a precise
878+
* position.
879+
*/
880+
POSITION_RESOLUTION_REQUESTED;
862881
}
863882
}

opentcs-documentation/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,9 @@ task configdocgen {
276276
"org.opentcs.kernel.SslConfiguration",
277277
"${configDocDir}/KernelSslConfigurationEntries.adoc",
278278

279+
"org.opentcs.kernel.vehicles.VehiclePositionResolverConfiguration",
280+
"${configDocDir}/VehiclePositionResolverConfigurationEntries.adoc",
281+
279282
"org.opentcs.virtualvehicle.VirtualVehicleConfiguration",
280283
"${configDocDir}/VirtualVehicleConfigurationEntries.adoc",
281284

opentcs-documentation/src/docs/release-notes/changelog.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ This change log lists the most relevant changes for past releases in reverse chr
2222
** Add new version 7.0.0 of the XML Schema definition for the openTCS plant model.
2323
** Add a watchdog task to the kernel which periodically monitors the state of transport orders.
2424
This watchdog task publishes a user notification in case a transport order is considered _idle_ (i.e., a transport order that has been waiting for a configurable amount of time to be processed by a vehicle) or in case the order's deadline has expired and the order is not yet in a final state.
25+
** Add support for determining a vehicle's (logical) position based on the vehicle's reported precise position.
26+
Vehicle drivers can now request this via `VehicleProcessModel.positionMappingRequested(Pose)`.
2527
** Update web API specification and implementation to version 1.13.0:
2628
*** Eliminate admin web API and move its endpoints to `GET /kernel/version` and `DELETE /kernel` in the service web API.
2729
*** Extend the `GET /transportOrders` and `GET /transportOrders/{NAME}` endpoints to include the complete transport order information in the response.

opentcs-documentation/src/docs/users-guide/05_configuring-opentcs.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ The kernel's SSL encryption can be configured using the following configuration
9292

9393
include::{configdoc}/KernelSslConfigurationEntries.adoc[]
9494

95+
==== Vehicle position resolver configuration entries
96+
97+
The vehicle position resolver can be configured using the following configuration entries:
98+
99+
include::{configdoc}/VehiclePositionResolverConfigurationEntries.adoc[]
100+
95101
==== Virtual vehicle configuration entries
96102

97103
The virtual vehicle (loopback communication adapter) can be configured using the following configuration entries:

opentcs-kernel/src/guiceConfig/java/org/opentcs/kernel/DefaultKernelInjectionModule.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
import org.opentcs.kernel.vehicles.VehicleCommAdapterRegistry;
6868
import org.opentcs.kernel.vehicles.VehicleControllerComponentsFactory;
6969
import org.opentcs.kernel.vehicles.VehicleControllerFactory;
70+
import org.opentcs.kernel.vehicles.VehiclePositionResolverConfiguration;
7071
import org.opentcs.kernel.vehicles.transformers.CoordinateSystemMapperFactory;
7172
import org.opentcs.kernel.vehicles.transformers.CoordinateSystemTransformerFactory;
7273
import org.opentcs.kernel.vehicles.transformers.DefaultVehicleDataTransformerFactory;
@@ -225,6 +226,13 @@ private void configureVehicleControllers() {
225226
.to(DefaultVehicleControllerPool.class);
226227
bind(LocalVehicleControllerPool.class)
227228
.to(DefaultVehicleControllerPool.class);
229+
bind(VehiclePositionResolverConfiguration.class)
230+
.toInstance(
231+
getConfigBindingProvider().get(
232+
VehiclePositionResolverConfiguration.PREFIX,
233+
VehiclePositionResolverConfiguration.class
234+
)
235+
);
228236
}
229237

230238
private void configurePeripheralControllers() {

opentcs-kernel/src/main/java/org/opentcs/kernel/vehicles/DefaultVehicleController.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ public class DefaultVehicleController
152152
* A transformer transforming incoming poses.
153153
*/
154154
private final IncomingPoseTransformer incomingPoseTransformer;
155+
/**
156+
* Resolves the vehicle precise position to an openTCS point.
157+
*/
158+
private final VehiclePositionResolver vehiclePositionResolver;
155159
/**
156160
* A map of transformed movement commands to their corresponding original ones.
157161
*/
@@ -174,6 +178,7 @@ public class DefaultVehicleController
174178
* @param configuration The configuration to use.
175179
* @param commandProcessingTracker Track processing of movement commands.
176180
* @param dataTransformerRegistry A registry for data transformer factories.
181+
* @param vehiclePositionResolver Resolves the vehicle precise position to an openTCS point.
177182
*/
178183
@Inject
179184
public DefaultVehicleController(
@@ -205,7 +210,9 @@ public DefaultVehicleController(
205210
@Nonnull
206211
CommandProcessingTracker commandProcessingTracker,
207212
@Nonnull
208-
VehicleDataTransformerRegistry dataTransformerRegistry
213+
VehicleDataTransformerRegistry dataTransformerRegistry,
214+
@Nonnull
215+
VehiclePositionResolver vehiclePositionResolver
209216
) {
210217
this.vehicle = requireNonNull(vehicle, "vehicle");
211218
this.commAdapter = requireNonNull(adapter, "adapter");
@@ -231,6 +238,8 @@ public DefaultVehicleController(
231238
= dataTransformerRegistry
232239
.findFactoryFor(vehicle)
233240
.createIncomingPoseTransformer(vehicle);
241+
this.vehiclePositionResolver
242+
= requireNonNull(vehiclePositionResolver, "vehiclePositionResolver");
234243
}
235244

236245
@Override
@@ -959,12 +968,33 @@ else if (Objects.equals(
959968
)) {
960969
dispatcherService.withdrawByVehicle(vehicle.getReference(), (Boolean) evt.getNewValue());
961970
}
971+
else if (Objects.equals(
972+
evt.getPropertyName(),
973+
VehicleProcessModel.Attribute.POSITION_RESOLUTION_REQUESTED.name()
974+
)) {
975+
mapPrecisePositionToLocalPosition((Pose) evt.getNewValue());
976+
}
962977
}
963978

964979
private void withdrawPendingResourceAllocations() {
965980
scheduler.clearPendingAllocations(this);
966981
}
967982

983+
private void mapPrecisePositionToLocalPosition(
984+
@Nonnull
985+
Pose pose
986+
)
987+
throws ObjectUnknownException {
988+
requireNonNull(pose, "pose");
989+
990+
setVehiclePosition(
991+
vehiclePositionResolver.resolveVehiclePosition(
992+
incomingPoseTransformer.apply(pose),
993+
(vehicle.getCurrentPosition() != null) ? vehicle.getCurrentPosition().getName() : null
994+
)
995+
);
996+
}
997+
968998
private void updateVehiclePose(
969999
@Nonnull
9701000
Pose pose
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// SPDX-FileCopyrightText: The openTCS Authors
2+
// SPDX-License-Identifier: MIT
3+
package org.opentcs.kernel.vehicles;
4+
5+
import static java.lang.Math.toDegrees;
6+
import static java.util.Objects.requireNonNull;
7+
8+
import jakarta.annotation.Nonnull;
9+
import jakarta.annotation.Nullable;
10+
import jakarta.inject.Inject;
11+
import org.opentcs.components.kernel.services.TCSObjectService;
12+
import org.opentcs.data.model.Point;
13+
import org.opentcs.data.model.Pose;
14+
15+
/**
16+
* Resolves the vehicle precise position to an openTCS point.
17+
*/
18+
public class VehiclePositionResolver {
19+
/**
20+
* The object service.
21+
*/
22+
private final TCSObjectService objectService;
23+
/**
24+
* This class' configuration.
25+
*/
26+
private final VehiclePositionResolverConfiguration configuration;
27+
28+
/**
29+
* Creates a new instance.
30+
*
31+
* @param objectService Object service.
32+
* @param configuration The configuration.
33+
*/
34+
@Inject
35+
public VehiclePositionResolver(
36+
@Nonnull
37+
TCSObjectService objectService,
38+
@Nonnull
39+
VehiclePositionResolverConfiguration configuration
40+
) {
41+
this.objectService = requireNonNull(objectService, "objectService");
42+
this.configuration = requireNonNull(configuration, "configuration");
43+
}
44+
45+
/**
46+
* Resolves the given precise position to the name of a point in the plant model.
47+
* <p>
48+
* If provided, the last known position is first checked to see if it already matches the
49+
* given precise position. If this check succeeds, the last known position is returned. If
50+
* the last known position is not provided or does not match the provided precise position,
51+
* the name of the first point in the plant model that matches the given precise position is
52+
* returned.
53+
* </p>
54+
*
55+
* @param lastKnownPosition The name of the vehicle's last known position. May be {@code null}
56+
* if the last position is not known.
57+
* @param precisePosition The vehicle's current precise position.
58+
* @return The name of the resolved point or the last known position if no point could be
59+
* found.
60+
*/
61+
@Nullable
62+
public String resolveVehiclePosition(
63+
@Nonnull
64+
Pose precisePosition,
65+
@Nullable
66+
String lastKnownPosition
67+
) {
68+
requireNonNull(precisePosition, "precisePosition");
69+
70+
if (isCurrentLogicalPositionCorrect(precisePosition, lastKnownPosition)) {
71+
return lastKnownPosition;
72+
}
73+
74+
for (Point p : objectService.fetch(Point.class)) {
75+
if (isPointAtPrecisePosition(p, precisePosition)) {
76+
return p.getName();
77+
}
78+
}
79+
return lastKnownPosition;
80+
}
81+
82+
private boolean isCurrentLogicalPositionCorrect(
83+
@Nonnull
84+
Pose precisePosition,
85+
@Nullable
86+
String lastKnownPosition
87+
) {
88+
if (lastKnownPosition == null) {
89+
return false;
90+
}
91+
92+
Point point = objectService.fetch(Point.class, lastKnownPosition).orElse(null);
93+
if (point == null) {
94+
return false;
95+
}
96+
97+
return isPointAtPrecisePosition(point, precisePosition);
98+
}
99+
100+
private boolean isPointAtPrecisePosition(Point p, Pose precisePosition) {
101+
return isWithinDeviationXY(p, precisePosition)
102+
&& isWithinDeviationTheta(p, precisePosition);
103+
}
104+
105+
private boolean isWithinDeviationXY(Point p, Pose precisePosition) {
106+
double deviationX = Math.abs(
107+
p.getPose().getPosition().getX()
108+
- precisePosition.getPosition().getX()
109+
);
110+
double deviationY = Math.abs(
111+
p.getPose().getPosition().getY()
112+
- precisePosition.getPosition().getY()
113+
);
114+
115+
return Math.sqrt((deviationX * deviationX) + (deviationY * deviationY))
116+
<= configuration.deviationXY();
117+
}
118+
119+
private boolean isWithinDeviationTheta(Point p, Pose precisePosition) {
120+
if (Double.isNaN(p.getPose().getOrientationAngle())) {
121+
return true;
122+
}
123+
return angleBetween(
124+
p.getPose().getOrientationAngle(), toDegrees(precisePosition.getOrientationAngle())
125+
) <= configuration.deviationTheta();
126+
}
127+
128+
private double angleBetween(double angle1, double angle2) {
129+
double difference = Math.abs(angle1 - angle2) % 360;
130+
if (difference > 180) {
131+
return Math.abs(difference - 360);
132+
}
133+
return difference;
134+
}
135+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// SPDX-FileCopyrightText: The openTCS Authors
2+
// SPDX-License-Identifier: MIT
3+
package org.opentcs.kernel.vehicles;
4+
5+
import org.opentcs.configuration.ConfigurationEntry;
6+
import org.opentcs.configuration.ConfigurationPrefix;
7+
8+
/**
9+
* Configuration for {@link VehiclePositionResolver}.
10+
*/
11+
@ConfigurationPrefix(VehiclePositionResolverConfiguration.PREFIX)
12+
public interface VehiclePositionResolverConfiguration {
13+
/**
14+
* This configuration's prefix.
15+
*/
16+
String PREFIX = "vehiclepositionresolver";
17+
18+
@ConfigurationEntry(
19+
type = "Integer",
20+
description = "The maximum allowed deviation of the x and y-coordinates between a logical"
21+
+ " position and the vehicle's actual precise position.",
22+
changesApplied = ConfigurationEntry.ChangesApplied.INSTANTLY,
23+
orderKey = "1_vehiclepositionresolver_0"
24+
)
25+
int deviationXY();
26+
27+
@ConfigurationEntry(
28+
type = "Integer",
29+
description = "The maximum allowed deviation of the orientation angle between a logical"
30+
+ " position and the vehicle's actual precise position.",
31+
changesApplied = ConfigurationEntry.ChangesApplied.INSTANTLY,
32+
orderKey = "1_vehiclepositionresolver_1"
33+
)
34+
int deviationTheta();
35+
}

opentcs-kernel/src/main/resources/org/opentcs/kernel/distribution/config/opentcs-kernel-defaults-baseline.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ virtualperipheral.enable = true
8585

8686
statisticscollector.enable = true
8787

88+
vehiclepositionresolver.deviationXY = 1
89+
vehiclepositionresolver.deviationTheta = 1
90+
8891
watchdog.blockConsistencyCheckInterval = 10000
8992
watchdog.strandedVehicleCheckInterval = 10000
9093
watchdog.strandedVehicleDurationThreshold = 60000

opentcs-kernel/src/test/java/org/opentcs/kernel/vehicles/DefaultVehicleControllerTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,8 @@ void setUp() {
173173
mock(MovementCommandMapper.class),
174174
mock(KernelApplicationConfiguration.class),
175175
new CommandProcessingTracker(),
176-
dataTransformerRegistry
176+
dataTransformerRegistry,
177+
mock(VehiclePositionResolver.class)
177178
);
178179
stdVehicleController.initialize();
179180
}

0 commit comments

Comments
 (0)