OptimizeToursRequest
applica vincoli a quanto segue:
- Spedizioni, che influiscono sulle modalità di spedizione
- Veicoli, che influisce sulla modalità di calcolo dei percorsi dei veicoli
- A livello globale, interessa sia i veicoli che le spedizioni.
Questa guida si concentra su un vincolo essenziale della spedizione: le finestre temporali.
Le finestre temporali sono un tipo di vincolo che fornisci nella
OptimizeToursRequest
messaggio (REST, gRPC) per specificare
i limiti temporali alle attività di spedizione. Questo tipo di vincolo influenza
sia quando e come può essere effettuata una spedizione sia l'assegnazione del veicolo
per la spedizione. Con questi vincoli, l'ottimizzatore dà la precedenza
i veicoli più adatti per soddisfare i limiti di tempo previsti per la spedizione.
Vincoli di spedizione: finestre temporali
Puoi specificare quando può avvenire il ritiro o la consegna in Shipment.VisitRequest
messaggio nel seguente modo:
- Utilizza la proprietà
timeWindows
nel messaggio (REST, gRPC) - Specifica l'ora di inizio e di fine nel messaggio
TimeWindow
(REST, gRPC).
Esempio di richiesta con vincoli di finestra temporale
L'esempio qui illustra tre diverse spedizioni, ciascuna con il proprio
finestra di consegna. Per semplicità, questo esempio imposta finestre temporali su deliveries
ma possono essere applicate anche ai ritiri. È possibile
anche se in questo esempio ne viene utilizzata solo una per pubblicazione VisitRequest
.
Visualizza un esempio di richiesta con finestre temporali
{ "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 } ] } }
Esempio di risposta con limiti di tempo
Nella risposta di esempio, l'ora di inizio e di fine del veicolo sono 17:35:50 e
rispettivamente 18:17:24. Questi tempi riflettono l'ottimizzatore che riduce al minimo il tempo
necessario per utilizzare il veicolo specificato nella richiesta come costPerHour
, mentre
che soddisfano tutti i vincoli di intervallo di tempo. Uso di 17:35:50 come ora di inizio.
elimina la necessità per il veicolo di attendere in un luogo di visita fino a quando
l'intervallo di tempo della visita. Nella risposta vengono visualizzati valori waitDuration
pari a zero.
Visualizza una risposta alla richiesta di esempio con finestre temporali
{ "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 } } }
Le finestre temporali hanno ordinato il visits
del veicolo in modo che le spedizioni con le finestre temporali più strette vengano consegnate per prime.
shipments[2]
viene consegnato alle 17:50shipments[1]
viene consegnato alle 18:00shipments[0]
viene consegnato alle 18:07
La richiesta di esempio specifica vincoli di finestre temporali fisiche, richiedendo
le consegne da completare all'interno di quelle finestre. Se il completamento del VisitRequests
di una spedizione entro una delle relative finestre di tempo non è fattibile o conveniente, l'ottimizzatore salta la spedizione. Se la spedizione include un
penaltyCost
, l'ottimizzatore la aggiunge ai costi riportati nella risposta
metrics
. In caso contrario, la proprietà skippedMandatoryShipmentCount
del
Aumenta il numero di messaggi OptimizeToursResponse
(REST, gRPC).
Se cambi le finestre temporali spostando la finestra di shipment[1]
di diverse ore
più tardi (alle 21:00 dalle 18:00), i risultati saranno diversi come illustrato nella
i seguenti esempi.
Visualizza un esempio di richiesta con finestre temporali che non possono sii soddisfatto
{ "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 } ] } }
Guarda una risposta alla seconda richiesta di esempio con finestre temporali in cui una spedizione viene saltata
{ "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 questo esempio, la finestra temporale successiva ha causato l'ignoramento di shipment[1]
,
poiché il tempo di funzionamento aggiuntivo del veicolo è necessario per completare
la consegna entro il periodo di tempo specificato ha superato il costo di penalità della spedizione.
Il costo della penalità per shipment[1]
viene visualizzato in metrics.costs
e il relativo indice
compare in skippedShipments
.
Vincoli temporali flessibili
Come accennato brevemente in Parametri del modello di costo, le finestre temporali possono essere applicate come vincoli soft. I vincoli soft si differenziano dai vincoli rigidi nel seguente modo:
- Vincoli rigidi: non possono essere violati e l'ottimizzatore non offre una soluzione che violi il vincolo, anche se ciò significa saltare una spedizione.
- Vincoli flessibili: potrebbero essere violati, il che significa che l'ottimizzatore potrebbe una soluzione che violi un vincolo soft. Tuttavia, l'ottimizzatore applica inoltre un costo a qualsiasi violazione. Fornisci questo costo come proprietà aggiuntiva nella finestra temporale, in genere come costo per ora per ogni ora prima o dopo la finestra temporale in cui si verifica l'attività.
Le finestre temporali vengono attenuate utilizzando softStartTime
o softEndTime
anziché startTime
o endTime
e impostando costPerHourBeforeSoftStartTime
o costPerHourAfterSoftEndTime
.
Utilizza vincoli temporali flessibili quando devono avvenire ritiri o consegne all'interno di un intervallo di tempo specificato, ma il ritiro o la consegna all'interno di tale finestra assolutamente necessario. Puoi utilizzare vincoli di tempo fissi e fissi insieme per esprimere gli scopi commerciali. Ad esempio:
- Finestra temporale fissa: indica l'orario di apertura di un cliente, ad esempio da Dalle 09:00 alle 17:00.
- Finestra temporale flessibile: indica i tempi per la consegna o il ritiro che corrisponde alla notifica inviata al cliente, ad esempio dalle 9:00 alle 13:00.
In questo esempio, la spedizione è stata saltata in precedenza perché l'orario finestra iniziata troppo tardi ha il vincolo dell'ora di inizio rallentata. Anche per le altre spedizioni sono stati modificati i periodi di tempo in cui è possibile effettuare l'ordine.
Visualizza un esempio di richiesta con tempi rigidi e flessibili finestre
{ "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 } ] } }
Visualizza una risposta alla richiesta di esempio con i comandi finestre temporali flessibili
{ "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 } } }
L'esempio con solo vincoli relativi alle finestre temporali difficili è stato ignorato
shipment[1]
, l'attenuazione del periodo di tempo per la consegna fa sì che venga pubblicato
prima dell'ora di inizio della finestra temporale. Analogamente, alleggerendo le ore di fine
altre spedizioni hanno consentito la consegna di shipment[2]
dopo la finestra temporale
termina il video.
Allo stesso tempo, i costi e le spedizioni totali sono cambiati:
totalCost
: diminuito da 81.283 a 64.797- spedizioni completate totali: aumentate da 2 a 3
L'ottimizzatore ha trovato una soluzione meno costosa perché la finestra temporale i vincoli sono stati ridotti rispetto all'esempio precedente.
Infine, la proprietà metrics.costs
include anche una nuova chiave per indicare
costo effettivo sostenuto in base al prodotto del vincolo e alla lunghezza
al momento del mancato rispetto della finestra di consegna. Ossia:
costPerHourBeforeSoftStartTime
di 2,0 e- il tempo che intercorre tra la pubblicazione effettiva e l'inizio dell'intervallo di tempo: 2,83583 ore
Risultato:
model.shipments.deliveries.time_windows.cost_per_hour_before_soft_start_time
:
5,6716666666666669.
Queste metriche ti consentono di eseguire un'analisi dei costi per vedere il compromesso tra vincoli rigidi e vincoli flessibili, che puoi utilizzare per ottimizzare i vincoli in modo che si adattino meglio alle tue regole aziendali specifiche. In questo caso, il costo totale è
inferiore a shipment[1].penalty_cost
di 20.0. L'ottimizzatore ha identificato
che è più conveniente consegnare la spedizione in anticipo rispetto
saltare la spedizione.