OptimizeToursRequest
applique des contraintes aux éléments suivants:
- Expéditions, affectant le déroulement des expéditions
- Véhicules, affectant la façon dont les itinéraires des véhicules sont calculés
- Dans le monde entier, ce qui concerne à la fois les véhicules et les livraisons.
Ce guide se concentre sur une contrainte d'expédition essentielle: les périodes de disponibilité.
Les fenêtres de temps sont un type de contrainte que vous spécifiez dans le
OptimizeToursRequest
(REST, gRPC) afin de spécifier
des limites de temps pour
les activités d’expédition. Ce type de contrainte influence
quand et comment une expédition peut être effectuée, ainsi que l'attribution du véhicule
pour la livraison. Avec ces contraintes, l'optimiseur donne la préférence
les véhicules qui peuvent mieux répondre
aux contraintes de temps de l’expédition.
Contraintes d'expédition: périodes
Vous indiquez le moment où le retrait ou la livraison peuvent avoir lieu dans le Shipment.VisitRequest
s'affiche comme suit:
- Utilisez la propriété
timeWindows
dans le message (REST, gRPC). - Spécifiez les heures de début et de fin dans le message
TimeWindow
(REST, gRPC).
Exemple de requête avec des contraintes de période
Cet exemple présente trois livraisons différentes, chacune avec ses propres
de livraison. Par souci de simplicité, cet exemple définit des fenêtres temporelles pour deliveries
.
uniquement, mais des périodes peuvent également être appliquées aux retraits. Plusieurs fenêtres
de temps peuvent
être spécifié, bien que cet exemple n'en utilise qu'un par livraison VisitRequest
.
Voir un exemple de requête avec des fenêtres de temps
{ "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 } ] } }
Exemple de réponse avec des contraintes de fenêtre de temps
Dans l'exemple de réponse, les heures de début et de fin du véhicule sont 17:35:50 et
18:17:24 respectivement. Ces durées indiquent que l'optimiseur minimise le temps
nécessaire pour utiliser le véhicule spécifié dans la demande en tant que costPerHour
le respect de toutes les contraintes
de fenêtre temporelle. 17:35:50 comme heure de début
élimine le temps que le véhicule attende jusqu'à ce que le véhicule
la période de la visite commence. Il apparaît dans la réponse sous la forme zéro waitDuration
valeurs.
Consultez une réponse à l'exemple de requête avec fenêtres de temps
{ "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 } } }
Les fenêtres de temps ont commandé le visits
du véhicule afin que les envois avec le
les périodes les plus anciennes
sont livrées en premier.
shipments[2]
est livré à 17h50shipments[1]
est livré à 18hshipments[0]
est livré à 18:07
L'exemple de requête spécifie des contraintes de fenêtre temporelle difficile, ce qui nécessite
livraisons doivent être
terminées dans ces fenêtres. Si vous terminez une expédition
VisitRequests
n'est pas réalisable au cours de l'une de ses périodes
rentable, l'optimiseur ignore l'expédition. Si le colis comporte
penaltyCost
, l'optimiseur l'ajoute aux coûts indiqués en réponse
metrics
Sinon, la propriété skippedMandatoryShipmentCount
de
Le message OptimizeToursResponse
(REST, gRPC) augmente.
Si vous modifiez les périodes en décalant celle de shipment[1]
de plusieurs heures
plus tard (à 21 h et de 18 h à 21 h), les résultats seront différents, comme illustré dans l'
les exemples suivants.
Voir un exemple de requête avec des fenêtres temporelles qui ne peuvent pas être satisfait
{ "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 } ] } }
Consultez la réponse au deuxième exemple de requête avec Périodes durant lesquelles une livraison est ignorée
{ "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 } } }
Dans cet exemple, la période ultérieure a entraîné le saut de shipment[1]
,
car le temps d'utilisation supplémentaire du véhicule
nécessaire pour terminer l'expédition
de livraison au cours de la période spécifiée a dépassé le coût de pénalité pour la livraison.
Le coût de la pénalité pour shipment[1]
apparaît dans metrics.costs
et son indice
apparaît dans skippedShipments
.
Contraintes de fenêtre flexible
Comme mentionné brièvement dans la section Paramètres du modèle de coût, il est possible d'appliquer des périodes en tant que contraintes soft. Les contraintes souples diffèrent des contraintes strictes de la manière suivante:
- Contraintes strictes: elles ne peuvent pas être enfreintes, et l'optimiseur n'offre pas de solution qui ne respecte pas la contrainte, même si cela implique d'ignorer livraison.
- Contraintes souples: elles peuvent être enfreintes, ce qui signifie que l'optimiseur peut fournir une solution qui ne respecte pas une contrainte souple. Cependant, l'optimiseur applique également un coût à tout cas de non-respect des règles. Vous indiquez ce coût sous forme de propriété supplémentaire dans la fenêtre temporelle, généralement sous la forme d'un coût par heure pour chaque heure avant ou après la période au cours de laquelle l'activité se produit.
Les fenêtres temporelles sont adoucies en utilisant softStartTime
ou softEndTime
au lieu de
startTime
ou endTime
respectivement, et en définissant
costPerHourBeforeSoftStartTime
ou costPerHourAfterSoftEndTime
.
Utilisez des contraintes de fenêtre horaire flexible lorsque les retraits ou les livraisons devraient avoir lieu dans un laps de temps donné, mais que le retrait ou la livraison absolument obligatoire. Vous pouvez utiliser conjointement des contraintes de fenêtre temporelle fixe et flexible pour exprimer les objectifs de l'entreprise. Exemple :
- Période difficile: indique les horaires d'ouverture d'un client (à partir de de 9h à 17h.
- Créneau horaire flexible: indique le délai de livraison ou de retrait correspond à la notification envoyée au client, par exemple de 9h à 13h.
Dans cet exemple, la livraison précédemment ignorée en raison de son heure de livraison qui a démarré trop tard et que sa contrainte d'heure de début s'est assouplie. L'autre les livraisons ont eu leurs fenêtres de temps les heures de fin sont également adoucies.
Voir un exemple de requête avec une période difficile et flexible fenêtres
{ "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 } ] } }
Voir une réponse à l'exemple de requête avec et fenêtres à durée flexible
{ "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 } } }
Dans cet exemple, l'exemple avec uniquement des contraintes de fenêtre de temps difficile a été complètement ignoré.
shipment[1]
, l'assouplissement du délai de livraison entraîne sa livraison
avant son heure de début. De même, l'adoucissement des heures de fin
autres envois ont autorisé la livraison de shipment[2]
après cette période
se termine.
Dans le même temps, les coûts et le nombre total d'expéditions ont changé:
totalCost
: a diminué de 81,283 à 64,797.- nombre total d'expéditions achevées: est passé de 2 à 3
L'optimiseur a trouvé une solution moins coûteuse, car la période les contraintes ont été assouplies par rapport à l'exemple précédent.
Enfin, la propriété metrics.costs
inclut également une nouvelle clé pour indiquer
coût réel encouru en fonction du produit de la contrainte et de la longueur de
l'heure à laquelle la période
de livraison a été manquée. Par exemple :
costPerHourBeforeSoftStartTime
sur 2,0 et- le délai entre la livraison réelle et le début de la période: 2,83583 heures
Résultat :
model.shipments.deliveries.time_windows.cost_per_hour_before_soft_start_time
:
5,6716666666666669.
Ces métriques vous permettent d'analyser les coûts afin d'identifier le compromis
et des contraintes souples, que vous pouvez utiliser pour ajuster vos contraintes
pour mieux s'adapter à vos règles métier spécifiques. Dans ce cas, le coût total est
moins de shipment[1].penalty_cost
de 20,0. L'optimiseur a identifié
qu'il est plus rentable de livrer la livraison tôt
ignorer la livraison.