OptimizeToursRequest
schränkt Folgendes ein:
- Sendungen, die sich auf die Ausführung von Sendungen auswirken
- Fahrzeuge, die sich auf die Berechnung von Fahrzeugrouten auswirken
- Betrifft weltweit sowohl Fahrzeuge als auch Sendungen.
In diesem Leitfaden geht es um eine wichtige Versandeinschränkung: Zeitfenster.
Zeitfenster sind eine Art von Einschränkung, die Sie in der OptimizeToursRequest
-Nachricht (REST, gRPC) angeben, um zeitbasierte Einschränkungen für Versandaktivitäten festzulegen. Diese Art von Einschränkung wirkt sich sowohl auf den Zeitpunkt und die Art der Lieferung als auch auf die Fahrzeugzuweisung für die Lieferung aus. Unter Berücksichtigung dieser Einschränkungen bevorzugt der Optimierer die Fahrzeuge, die die Zeitbeschränkungen der Sendung am besten erfüllen können.
Versandeinschränkungen: Zeitfenster
So legen Sie in der Shipment.VisitRequest
-Nachricht fest, wann eine Abholung oder Lieferung erfolgen kann:
- Verwenden Sie die Property
timeWindows
in der Nachricht (REST, gRPC). - Gib in der
TimeWindow
-Nachricht (REST, gRPC) den Start- und Endzeitpunkt an.
Beispielanfrage mit Zeitfenstereinschränkungen
Das Beispiel hier zeigt drei verschiedene Lieferungen mit jeweils einem eigenen Lieferzeitraum. Der Einfachheit halber werden in diesem Beispiel Zeitfenster nur für deliveries
festgelegt. Sie können aber auch auf Abholstellen angewendet werden. Es können mehrere Zeitfenster angegeben werden. In diesem Beispiel wird jedoch nur eines pro Auslieferung verwendet.VisitRequest
Beispielanfrage mit Zeitfenstern
{ "populatePolylines": false, "populateTransitionPolylines": false, "model": { "globalStartTime": "2023-01-13T16:00:00Z", "globalEndTime": "2023-01-14T16:00:00Z", "shipments": [ { "deliveries": [ { "arrivalLocation": { "latitude": 37.789456, "longitude": -122.390192 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T18:00:00Z", "endTime": "2023-01-13T19:00:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 100.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.789116, "longitude": -122.395080 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T18:00:00Z", "endTime": "2023-01-13T18:30:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 20.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.795242, "longitude": -122.399347 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T17:30:00Z", "endTime": "2023-01-13T18:00:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 50.0 } ], "vehicles": [ { "endLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "startLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "costPerHour": 40.0, "costPerKilometer": 10.0 } ] } }
Beispiel für eine Antwort mit Einschränkungen für Zeitfenster
In der Beispielantwort sind das Start- und Enddatum des Fahrzeugs 17:35:50 und 18:17:24. Diese Zeiten spiegeln den Optimierer wider, der die Zeit minimiert, die für den Betrieb des in der Anfrage als costPerHour
angegebenen Fahrzeugs erforderlich ist, und dabei alle Zeitfenstereinschränkungen erfüllt. Wenn Sie 17:35:50 als Startzeit verwenden, muss das Fahrzeug nicht am Besuchsort warten, bis das Zeitfenster für den Besuch beginnt. In der Antwort werden dann Nullwerte für waitDuration
angezeigt.
Beispielantwort mit Zeitfenstern ansehen
{ "routes": [ { "vehicleStartTime": "2023-01-13T17:35:50Z", "vehicleEndTime": "2023-01-13T18:17:24Z", "visits": [ { "isPickup": true, "startTime": "2023-01-13T17:35:50Z", "detour": "0s" }, { "shipmentIndex": 1, "isPickup": true, "startTime": "2023-01-13T17:38:20Z", "detour": "150s" }, { "shipmentIndex": 2, "isPickup": true, "startTime": "2023-01-13T17:40:50Z", "detour": "300s" }, { "shipmentIndex": 2, "startTime": "2023-01-13T17:50:09Z", "detour": "0s" }, { "shipmentIndex": 1, "startTime": "2023-01-13T18:00:00Z", "detour": "796s" }, { "startTime": "2023-01-13T18:07:35Z", "detour": "1520s" } ], "transitions": [ { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:35:50Z" }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:38:20Z" }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:40:50Z" }, { "travelDuration": "409s", "travelDistanceMeters": 1371, "waitDuration": "0s", "totalDuration": "409s", "startTime": "2023-01-13T17:43:20Z" }, { "travelDuration": "341s", "travelDistanceMeters": 1312, "waitDuration": "0s", "totalDuration": "341s", "startTime": "2023-01-13T17:54:19Z" }, { "travelDuration": "205s", "travelDistanceMeters": 636, "waitDuration": "0s", "totalDuration": "205s", "startTime": "2023-01-13T18:04:10Z" }, { "travelDuration": "339s", "travelDistanceMeters": 1276, "waitDuration": "0s", "totalDuration": "339s", "startTime": "2023-01-13T18:11:45Z" } ], "metrics": { "performedShipmentCount": 3, "travelDuration": "1294s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2494s", "travelDistanceMeters": 4595 }, "routeCosts": { "model.vehicles.cost_per_hour": 27.711111111111112, "model.vehicles.cost_per_kilometer": 45.95 }, "routeTotalCost": 73.661111111111111 } ], "metrics": { "aggregatedRouteMetrics": { "performedShipmentCount": 3, "travelDuration": "1294s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2494s", "travelDistanceMeters": 4595 }, "usedVehicleCount": 1, "earliestVehicleStartTime": "2023-01-13T17:35:50Z", "latestVehicleEndTime": "2023-01-13T18:17:24Z", "totalCost": 73.661111111111111, "costs": { "model.vehicles.cost_per_hour": 27.711111111111112, "model.vehicles.cost_per_kilometer": 45.95 } } }
Die visits
des Fahrzeugs wurden nach Zeitfenstern sortiert, sodass die Sendungen mit den frühesten Zeitfenstern zuerst geliefert werden.
shipments[2]
wird um 17:50 Uhr zugestellt.shipments[1]
wird um 18:00 Uhr geliefertshipments[0]
wird um 18:07 Uhr gesendet
In der Beispielanfrage sind strenge Zeitfensterbeschränkungen angegeben, die besagen, dass Übermittlungen innerhalb dieser Zeitfenster abgeschlossen werden müssen. Wenn die VisitRequests
einer Sendung nicht innerhalb eines Zeitfensters abgeschlossen werden kann, ist dies nicht möglich oder nicht kostengünstig. In diesem Fall überspringt der Optimierer die Sendung. Wenn die Sendung eine penaltyCost
hat, wird sie vom Optimierer zu den in der Antwort metrics
angegebenen Kosten addiert. Andernfalls wird die skippedMandatoryShipmentCount
-Eigenschaft der OptimizeToursResponse
-Nachricht (REST, gRPC) erhöht.
Wenn Sie die Zeitfenster ändern, indem Sie das Fenster für shipment[1]
um mehrere Stunden verschieben (von 18:00 Uhr auf 21:00 Uhr), unterscheiden sich die Ergebnisse, wie in den folgenden Beispielen dargestellt.
Beispielanfrage mit Zeitfenstern, die nicht erfüllt werden können
{ "populatePolylines": false, "populateTransitionPolylines": false, "model": { "globalStartTime": "2023-01-13T16:00:00Z", "globalEndTime": "2023-01-14T16:00:00Z", "shipments": [ { "deliveries": [ { "arrivalLocation": { "latitude": 37.789456, "longitude": -122.390192 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T18:00:00Z", "endTime": "2023-01-13T19:00:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 100.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.789116, "longitude": -122.395080 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T21:00:00Z", "endTime": "2023-01-13T21:30:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 20.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.795242, "longitude": -122.399347 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T17:30:00Z", "endTime": "2023-01-13T18:00:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 50.0 } ], "vehicles": [ { "endLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "startLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "costPerHour": 40.0, "costPerKilometer": 10.0 } ] } }
Antwort auf die zweite Beispielanfrage mit Zeitfenstern, in denen eine Sendung übersprungen wird
{ "routes": [ { "vehicleStartTime": "2023-01-13T17:37:49Z", "vehicleEndTime": "2023-01-13T18:09:49Z", "visits": [ { "isPickup": true, "startTime": "2023-01-13T17:37:49Z", "detour": "0s" }, { "shipmentIndex": 2, "isPickup": true, "startTime": "2023-01-13T17:40:19Z", "detour": "150s" }, { "shipmentIndex": 2, "startTime": "2023-01-13T17:49:38Z", "detour": "0s" }, { "startTime": "2023-01-13T18:00:00Z", "detour": "946s" } ], "transitions": [ { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:37:49Z" }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:40:19Z" }, { "travelDuration": "409s", "travelDistanceMeters": 1371, "waitDuration": "0s", "totalDuration": "409s", "startTime": "2023-01-13T17:42:49Z" }, { "travelDuration": "372s", "travelDistanceMeters": 1348, "waitDuration": "0s", "totalDuration": "372s", "startTime": "2023-01-13T17:53:48Z" }, { "travelDuration": "339s", "travelDistanceMeters": 1276, "waitDuration": "0s", "totalDuration": "339s", "startTime": "2023-01-13T18:04:10Z" } ], "metrics": { "performedShipmentCount": 2, "travelDuration": "1120s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "800s", "totalDuration": "1920s", "travelDistanceMeters": 3995 }, "routeCosts": { "model.vehicles.cost_per_kilometer": 39.95, "model.vehicles.cost_per_hour": 21.333333333333332 }, "routeTotalCost": 61.283333333333331 } ], "skippedShipments": [ { "index": 1 } ], "metrics": { "aggregatedRouteMetrics": { "performedShipmentCount": 2, "travelDuration": "1120s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "800s", "totalDuration": "1920s", "travelDistanceMeters": 3995 }, "usedVehicleCount": 1, "earliestVehicleStartTime": "2023-01-13T17:37:49Z", "latestVehicleEndTime": "2023-01-13T18:09:49Z", "totalCost": 81.283333333333331, "costs": { "model.shipments.penalty_cost": 20, "model.vehicles.cost_per_hour": 21.333333333333332, "model.vehicles.cost_per_kilometer": 39.95 } } }
In diesem Beispiel wurde shipment[1]
aufgrund des späteren Zeitfensters übersprungen, da die zusätzliche Betriebszeit des Fahrzeugs, die für die Lieferung der Sendung innerhalb des angegebenen Zeitfensters erforderlich war, die Strafkosten der Sendung überstieg.
Die Strafkosten für shipment[1]
werden in metrics.costs
und der Index in skippedShipments
angezeigt.
Weiche Zeitfenstereinschränkungen
Wie bereits kurz unter Parameter des Kostenmodells erwähnt, können Zeitfenster als weiche Einschränkungen angewendet werden. Weiche Einschränkungen unterscheiden sich von harten Einschränkungen folgendermaßen:
- Starre Einschränkungen: Sie dürfen nicht verletzt werden und der Optimierer bietet keine Lösung an, die gegen die Einschränkung verstößt, auch wenn dadurch eine Lieferung übersprungen wird.
- Weniger strikte Einschränkungen: Diese können verletzt werden. Das bedeutet, dass der Optimierer eine Lösung liefern kann, die gegen eine weniger strikte Einschränkung verstößt. Der Optimierer wendet jedoch auch einen Kostenwert auf jeden Verstoß an. Sie geben diese Kosten als zusätzliche Property im Zeitfenster an, in der Regel als Kosten pro Stunde für jede Stunde vor oder nach dem Zeitfenster, in dem die Aktivität stattfindet.
Zeitfenster werden weicher, wenn Sie softStartTime
oder softEndTime
anstelle von startTime
oder endTime
verwenden und costPerHourBeforeSoftStartTime
oder costPerHourAfterSoftEndTime
festlegen.
Verwenden Sie weiche Zeitfensterbeschränkungen, wenn Abholungen oder Lieferungen sollten innerhalb eines bestimmten Zeitfensters erfolgen, die Abholung oder Lieferung innerhalb dieses Zeitfensters aber nicht unbedingt erforderlich ist. Sie können feste und flexible Zeitfensterbeschränkungen kombinieren, um Geschäftsziele zu formulieren. Beispiel:
- Festes Zeitfenster: Gibt die Öffnungszeiten eines Kunden an, z. B. von 9:00 bis 17:00 Uhr.
- Weniger strikter Zeitrahmen: Gibt den Zeitraum für die Lieferung oder Abholung an, der mit der an den Kunden gesendeten Benachrichtigung übereinstimmt, z. B. 9:00 bis 13:00 Uhr.
In diesem Beispiel wurde die Einschränkung für die Startzeit der Sendung, die zuvor übersprungen wurde, weil ihr Zeitfenster zu spät begann, gelockert. Auch die Endzeiten der Zeitfenster für die anderen Sendungen wurden verlängert.
Beispielanfrage mit festen und flexiblen Zeitfenstern
{ "populatePolylines": false, "populateTransitionPolylines": false, "model": { "globalStartTime": "2023-01-13T16:00:00Z", "globalEndTime": "2023-01-14T16:00:00Z", "shipments": [ { "deliveries": [ { "arrivalLocation": { "latitude": 37.789456, "longitude": -122.390192 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T18:00:00Z", "softEndTime": "2023-01-13T19:00:00Z", "costPerHourAfterSoftEndTime": 2.0 } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 100.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.789116, "longitude": -122.395080 }, "duration": "250s", "timeWindows": [ { "softStartTime": "2023-01-13T21:00:00Z", "endTime": "2023-01-13T21:30:00Z", "costPerHourBeforeSoftStartTime": 2.0 } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 20.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.795242, "longitude": -122.399347 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T17:30:00Z", "softEndTime": "2023-01-13T18:00:00Z", "costPerHourAfterSoftEndTime": 2.0 } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 50.0 } ], "vehicles": [ { "endLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "startLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "costPerHour": 40.0, "costPerKilometer": 10.0 } ] } }
Antwort auf die Beispielanfrage mit festen und flexiblen Zeitfenstern ansehen
{ "routes": [ { "vehicleStartTime": "2023-01-13T17:48:35Z", "vehicleEndTime": "2023-01-13T18:24:28Z", "visits": [ { "isPickup": true, "startTime": "2023-01-13T17:48:35Z", "detour": "0s" }, { "shipmentIndex": 1, "isPickup": true, "startTime": "2023-01-13T17:51:05Z", "detour": "150s" }, { "shipmentIndex": 2, "isPickup": true, "startTime": "2023-01-13T17:53:35Z", "detour": "300s" }, { "startTime": "2023-01-13T18:00:00Z", "detour": "300s" }, { "shipmentIndex": 1, "startTime": "2023-01-13T18:07:42Z", "detour": "493s" }, { "shipmentIndex": 2, "startTime": "2023-01-13T18:17:27Z", "detour": "873s" } ], "transitions": [ { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:48:35Z" }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:51:05Z" }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:53:35Z" }, { "travelDuration": "235s", "travelDistanceMeters": 795, "waitDuration": "0s", "totalDuration": "235s", "startTime": "2023-01-13T17:56:05Z" }, { "travelDuration": "212s", "travelDistanceMeters": 791, "waitDuration": "0s", "totalDuration": "212s", "startTime": "2023-01-13T18:04:10Z" }, { "travelDuration": "335s", "travelDistanceMeters": 1204, "waitDuration": "0s", "totalDuration": "335s", "startTime": "2023-01-13T18:11:52Z" }, { "travelDuration": "171s", "travelDistanceMeters": 665, "waitDuration": "0s", "totalDuration": "171s", "startTime": "2023-01-13T18:21:37Z" } ], "metrics": { "performedShipmentCount": 3, "travelDuration": "953s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2153s", "travelDistanceMeters": 3455 }, "routeCosts": { "model.shipments.deliveries.time_windows.cost_per_hour_after_soft_end_time": 0.58166666666666667, "model.shipments.deliveries.time_windows.cost_per_hour_before_soft_start_time": 5.7433333333333332, "model.vehicles.cost_per_hour": 23.922222222222221, "model.vehicles.cost_per_kilometer": 34.55 }, "routeTotalCost": 64.797222222222217 } ], "metrics": { "aggregatedRouteMetrics": { "performedShipmentCount": 3, "travelDuration": "953s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2153s", "travelDistanceMeters": 3455 }, "usedVehicleCount": 1, "earliestVehicleStartTime": "2023-01-13T17:48:35Z", "latestVehicleEndTime": "2023-01-13T18:24:28Z", "totalCost": 64.797222222222217, "costs": { "model.vehicles.cost_per_kilometer": 34.55, "model.shipments.deliveries.time_windows.cost_per_hour_before_soft_start_time": 5.7433333333333332, "model.shipments.deliveries.time_windows.cost_per_hour_after_soft_end_time": 0.58166666666666667, "model.vehicles.cost_per_hour": 23.922222222222221 } } }
Im Beispiel mit nur strikten Zeitfenstereinschränkungen wurde shipment[1]
vollständig übersprungen. Wenn das Zeitfenster für die Auslieferung jedoch gelockert wird, wird es vor Beginn des Zeitfensters zugestellt. Durch die Verlängerung der Endzeiten der anderen Sendungen konnte shipment[2]
auch nach Ablauf des Zeitfensters geliefert werden.
Gleichzeitig haben sich sowohl die Kosten als auch die Gesamtzahl der Sendungen geändert:
totalCost
: gesunken von 81.283 auf 64.797- Anzahl der abgeschlossenen Sendungen insgesamt: gestiegen von 2 auf 3
Der Optimierer hat eine kostengünstigere Lösung gefunden, da die Einschränkungen für das Zeitfenster im Vergleich zum vorherigen Beispiel gelockert wurden.
Schließlich enthält die Property vom Typ metrics.costs
einen neuen Schlüssel, der die tatsächlichen Kosten angibt, die auf der Grundlage des Produkts der Einschränkung und der Dauer des verpassten Übermittlungszeitraums anfallen. Das bedeutet:
costPerHourBeforeSoftStartTime
von 2.0 und- die Zeit zwischen der tatsächlichen Auslieferung und dem Beginn des Zeitfensters: 2,83583 Stunden
Ergebnis:
model.shipments.deliveries.time_windows.cost_per_hour_before_soft_start_time
:
5,6716666666666669.
Mit diesen Messwerten können Sie eine Kostenanalyse durchführen, um den Trade-off zwischen strengen und flexiblen Einschränkungen zu sehen. So können Sie Ihre Einschränkungen so anpassen, dass sie besser zu Ihren spezifischen Geschäftsregeln passen. In diesem Fall sind die Gesamtkosten niedriger als shipment[1].penalty_cost
= 20, 0. Der Optimierer hat festgestellt, dass es kostengünstiger ist, die Sendung vorzeitig zuzustellen, als sie zu überspringen.