OptimizeToursRequest
applique des contraintes aux éléments suivants:
- les expéditions, qui ont une incidence sur la façon dont elles sont effectuées ;
- Véhicules, qui affectent le calcul des itinéraires
- À l'échelle mondiale, cela affecte à la fois les véhicules et les envois.
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 l'heure de début et de fin dans le message
TimeWindow
(REST, gRPC).
Exemple de requête avec des contraintes de période
L'exemple ci-dessous illustre trois envois différents, chacun avec sa propre période 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 période
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 } ] } }
Voir une réponse à la deuxième requête d'exemple avec des périodes, où une expédition 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 de livraison ultérieure a entraîné l'exclusion de shipment[1]
, car le temps de fonctionnement supplémentaire du véhicule requis pour effectuer la livraison de l'envoi dans la période spécifiée dépassait le coût de pénalité de l'envoi.
Le coût de la pénalité pour shipment[1]
apparaît dans metrics.costs
et son indice
apparaît dans skippedShipments
.
Contraintes de période flexibles
Comme indiqué brièvement dans la section Paramètres du modèle de coût, les périodes peuvent être appliquées en tant que contraintes souples. Les contraintes souples diffèrent des contraintes strictes de la manière suivante:
- Contraintes strictes : ne peuvent pas être violées, et l'optimiseur n'offre pas de solution qui viole la contrainte, même si cela signifie ignorer un envoi.
- Contraintes souples : peuvent être violées, 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 période flexibles lorsque les collectes ou les livraisons devraient avoir lieu dans une période spécifiée, mais que la collecte ou la livraison dans cette période n'est pas 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.
- Plage horaire flexible : indique la période de livraison ou de retrait correspondant à 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 leur fenêtre 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 temporelle 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'assouplissement des heures de fin des autres envois a permis à shipment[2]
d'être livré après la fin de sa période.
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 le coût réel en fonction du produit de la contrainte et de la durée pendant 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.