Questa guida descrive loadDemands
e loadLimits
e il loro rapporto tra loro.
Come indicato in Limiti della finestra temporale di ritiro e consegna, il messaggioOptimizeToursRequest
(REST, gRPC) contiene una serie di proprietà che specificano i limiti del problema da ottimizzare. Diverse proprietà
OptimizeToursRequest
rappresentano limitazioni di carico.
I veicoli e le spedizioni hanno proprietà fisiche che devono essere prese in considerazione quando si pianifica un itinerario.
- Veicoli: la proprietà
loadLimits
specifica il carico massimo che il veicolo può gestire. Consulta la documentazione del messaggioVehicle
(REST, gRPC). - Spedizioni: la proprietà
loadDemands
specifica il carico consumato da una determinata spedizione. Consulta la documentazione del messaggioShipment
(REST, gRPC).
Insieme, questi due vincoli consentono all'ottimizzatore di assegnare in modo appropriato le spedizioni ai veicoli in modo che corrispondano al meglio alla capacità del parco veicoli e alle richieste di spedizione.
Il resto del documento illustra loadLimits
e loadDemands
in dettaglio.
Carichi e limiti: tipi
Ogni vincolo di limite e domanda di carico viene espresso in termini di tipo.
Puoi fornire il tuo set di tipi di caricamento, come gli esempi riportati di seguito:
- peso
- volume
- misurazioni lineari
- nomi di articoli o attrezzature trasportati
Questa guida utilizza weightKg
come tipo di esempio.
Sia Shipment.loadDemands
che Vehicle.loadLimits
utilizzano il tipo Protocol Buffers
map
, con chiavi string
che rappresentano i tipi di caricamento.
I valori Shipment.loadDemands
utilizzano il messaggio Load
(REST, gRPC).
Il messaggio Load
ha una singola proprietà amount
che rappresenta la quantità di capacità necessaria per completare la spedizione nel tipo specificato.
I valori Vehicle.loadLimits
utilizzano il messaggio LoadLimit
(REST,
gRPC). Il messaggio LoadLimit
ha diverse proprietà, con maxLoad
che rappresenta la capacità di carico massima del veicolo nel tipo specificato.
Il loadDemands
di una spedizione consuma il loadLimits
del veicolo assegnato solo se i due hanno chiavi di tipo di carico corrispondenti. Ad esempio, una spedizione con
loadDemands
di:
"loadDemands": {
"weightKg": {
"amount": 50
}
}
per il completamento della spedizione sono necessarie 50 unità di carico del tipo weightKg
. Un veicolo con loadLimits
di:
"loadLimits": {
"weightKg": {
"maxLoad": 100
}
}
potrebbe essere in grado di completare la spedizione, poiché il valore maxLoad
del veicolo nel tipo weightKg
è maggiore o uguale al valore loadDemands
della spedizione nel tipo weightKg
. Tuttavia, un veicolo con loadLimits
di:
"loadLimits": {
"equipmentRackStorage": {
"maxLoad": 10
}
}
ha implicitamente una capacità weightKg
illimitata a causa dell'assenza di un limite di carico weightKg
, pertanto il veicolo non è vincolato dalla domanda di peso della spedizione.
Trasferimento del carico tra spedizioni e veicoli
Quando le spedizioni vengono ritirate e consegnate dai veicoli, il valore loadDemand
della spedizione viene trasferito tra la spedizione e il veicolo. Puoi visualizzare i carichi del veicolo nella voce routes.transitions
del messaggio OptimizeToursResponse
(REST,
gRPC) per un determinato veicolo. La sequenza è la seguente:
- La capacità di carico richiesta è definita per la spedizione come
loadDemand
. - La spedizione viene ritirata dal veicolo a cui è assegnata e il
vehicleLoads
del veicolo aumenta dell'importo delloadDemand
della spedizione. Questo trasferimento è rappresentato davisits.loadDemands
positivo nel messaggio di risposta. - Il veicolo consegna la spedizione e il relativo
vehicleLoads
diminuisce dell'importo delloadDemand
della spedizione consegnata. Questo trasferimento è rappresentato davisits.loadDemands
negativo nel messaggio di risposta.
Il vehicleLoads
di un veicolo non può superare il loadLimits
specificato in nessun punto del percorso.
Un esempio completo con richieste di carico e limiti
Visualizza un esempio di richiesta con richieste di carico e limiti
{ "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" } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 100.0, "loadDemands": { "weightKg": { "amount": 50 } } }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.789116, "longitude": -122.395080 }, "duration": "250s" } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 15.0, "loadDemands": { "weightKg": { "amount": 10 } } }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.795242, "longitude": -122.399347 }, "duration": "250s" } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 50.0, "loadDemands": { "weightKg": { "amount": 80 } } } ], "vehicles": [ { "endLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "startLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "costPerHour": 40.0, "costPerKilometer": 10.0, "loadLimits": { "weightKg": { "maxLoad": 100 } } } ] } }
La richiesta di esempio contiene diversi parametri relativi al caricamento:
shipments[0]
ha una richiesta di carico di 50weightKg
.shipments[1]
ha una domanda di carico di 10weightKg
.shipments[2]
ha una domanda di carico di 80weightKg
.vehicles[0]
ha un limite di caricamento di 100weightKg
.
Visualizza una risposta alla richiesta con richieste di carico e limiti
{ "routes": [ { "vehicleStartTime": "2023-01-13T16:00:00Z", "vehicleEndTime": "2023-01-13T16:43:27Z", "visits": [ { "isPickup": true, "startTime": "2023-01-13T16:00:00Z", "detour": "0s", "loadDemands": { "weightKg": { "amount": "50" } } }, { "shipmentIndex": 1, "isPickup": true, "startTime": "2023-01-13T16:02:30Z", "detour": "150s", "loadDemands": { "weightKg": { "amount": "10" } } }, { "startTime": "2023-01-13T16:08:55Z", "detour": "150s", "loadDemands": { "weightKg": { "amount": "-50" } } }, { "shipmentIndex": 1, "startTime": "2023-01-13T16:16:37Z", "detour": "343s", "loadDemands": { "weightKg": { "amount": "-10" } } }, { "shipmentIndex": 2, "isPickup": true, "startTime": "2023-01-13T16:27:07Z", "detour": "1627s", "loadDemands": { "weightKg": { "amount": "80" } } }, { "shipmentIndex": 2, "startTime": "2023-01-13T16:36:26Z", "detour": "0s", "loadDemands": { "weightKg": { "amount": "-80" } } } ], "transitions": [ { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T16:00:00Z", "vehicleLoads": { "weightKg": {} } }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T16:02:30Z", "vehicleLoads": { "weightKg": { "amount": "50" } } }, { "travelDuration": "235s", "travelDistanceMeters": 795, "waitDuration": "0s", "totalDuration": "235s", "startTime": "2023-01-13T16:05:00Z", "vehicleLoads": { "weightKg": { "amount": "60" } } }, { "travelDuration": "212s", "travelDistanceMeters": 791, "waitDuration": "0s", "totalDuration": "212s", "startTime": "2023-01-13T16:13:05Z", "vehicleLoads": { "weightKg": { "amount": "10" } } }, { "travelDuration": "380s", "travelDistanceMeters": 1190, "waitDuration": "0s", "totalDuration": "380s", "startTime": "2023-01-13T16:20:47Z", "vehicleLoads": { "weightKg": {} } }, { "travelDuration": "409s", "travelDistanceMeters": 1371, "waitDuration": "0s", "totalDuration": "409s", "startTime": "2023-01-13T16:29:37Z", "vehicleLoads": { "weightKg": { "amount": "80" } } }, { "travelDuration": "171s", "travelDistanceMeters": 665, "waitDuration": "0s", "totalDuration": "171s", "startTime": "2023-01-13T16:40:36Z", "vehicleLoads": { "weightKg": {} } } ], "metrics": { "performedShipmentCount": 3, "travelDuration": "1407s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2607s", "travelDistanceMeters": 4812, "maxLoads": { "weightKg": { "amount": "80" } } }, "routeCosts": { "model.vehicles.cost_per_kilometer": 48.12, "model.vehicles.cost_per_hour": 28.966666666666665 }, "routeTotalCost": 77.086666666666659 } ], "metrics": { "aggregatedRouteMetrics": { "performedShipmentCount": 3, "travelDuration": "1407s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2607s", "travelDistanceMeters": 4812, "maxLoads": { "weightKg": { "amount": "80" } } }, "usedVehicleCount": 1, "earliestVehicleStartTime": "2023-01-13T16:00:00Z", "latestVehicleEndTime": "2023-01-13T16:43:27Z", "totalCost": 77.086666666666659, "costs": { "model.vehicles.cost_per_hour": 28.966666666666665, "model.vehicles.cost_per_kilometer": 48.12 } } }
I vincoli di caricamento aggiunti influiscono sull'ordine di visits
:
shipment[0]
viene ritiratoshipment[1]
viene ritiratoshipment[0]
viene pubblicatoshipment[1]
viene pubblicatoshipment[2]
viene ritiratoshipment[2]
viene pubblicato
Questo ordine indica che il veicolo non può completare tre spedizioni contemporaneamente perché il loro loadDemands
totale supera il loadLimits
del veicolo.
Ogni voce visits
include la variazione del carico del veicolo risultante dal completamento del Visit
. I valori di carico positivi rappresentano il caricamento della spedizione, mentre quelli negativi ne rappresentano lo scarico.
Ogni voce transitions
include il carico totale del veicolo durante il
Transition
. transitions[2]
, ad esempio, ha un valore weightKg
pari a 60,
che rappresenta i carichi combinati di shipment[0]
e shipment[1]
.
Gli oggetti metrica routes[0].metrics
e metrics.aggregatedRouteMetrics
includono
una proprietà maxLoads
. Il valore per il tipo weightKg
è 80 e rappresenta la parte del percorso del veicolo che ha trasportato shipments[2]
alla sua destinazione di consegna.
Vincoli di limite di carico soft
Come per le finestre temporali descritte in Vincoli relativi alle finestre temporali di ritiro e consegna, i vincoli relativi al limite di carico hanno varianti rigide e flessibili. La proprietà maxLoad
del messaggio LoadLimit
esprime un vincolo rigido: il veicolo non deve mai trasportare un carico superiore al valore maxLoad
nel tipo specificato. Le proprietà softMaxLoad
e costPerUnitAboveSoftMax
esprimono un vincolo soft, con ogni unità che supera softMaxLoad
che comporta un costo di costPerUnitAboveSoftMax
.
I vincoli di limite di carico soft hanno diversi utilizzi, ad esempio:
- bilanciare le spedizioni su più veicoli rispetto al numero minimo necessario se è conveniente farlo
- esprimere la preferenza del conducente per il numero di articoli che può ritirare e consegnare comodamente su un determinato percorso
- caricare i veicoli al di sotto della loro capacità fisica massima per limitare l'usura e ridurre i costi di manutenzione
I vincoli di limite di carico hard e soft possono essere utilizzati insieme. Ad esempio, un limite di carico rigido potrebbe indicare il peso massimo del carico che un veicolo può trasportare in sicurezza o il numero massimo di articoli che possono essere caricati contemporaneamente in un veicolo, mentre un limite di carico flessibile potrebbe essere il peso o il numero massimo di articoli che metterebbero alla prova la capacità del conducente di far stare tutto nel veicolo.