Richieste e limiti di carico

Questa guida descrive loadDemands e loadLimits e come si relazionano tra loro.

Come accennato in Vincoli per le finestre di tempo di ritiro e consegna, il messaggio OptimizeToursRequest (REST, gRPC) contiene una serie di proprietà che specificano i vincoli sul problema da ottimizzare. Diverse proprietà OptimizeToursRequest rappresentano i vincoli di caricamento.

I veicoli e le spedizioni hanno proprietà fisiche che devono essere considerate quando si pianifica un percorso.

  • Veicoli: la proprietà loadLimits specifica il carico massimo che il veicolo può gestire. Consulta la documentazione dei messaggi Vehicle (REST, gRPC).
  • Spedizioni: la proprietà loadDemands specifica il carico consumato da una determinata spedizione. Consulta la documentazione dei messaggi Shipment (REST, gRPC).

Insieme, questi due vincoli consentono all'ottimizzatore di assegnare in modo appropriato le spedizioni ai veicoli nel modo più adatto alla capacità della flotta e alle esigenze di spedizione.

Questa parte del documento illustra in dettaglio loadLimits e loadDemands.

Carica richieste e limiti: tipi

Puoi esprimere ogni vincolo di carico e limite in termini di tipo.

Puoi fornire il tuo insieme di tipi di carico, come nei seguenti esempi:

  • weight
  • volume
  • misurazioni lineari
  • nomi di oggetti o attrezzature trasportati

Questa guida utilizza weightKg come tipo di esempio.

Sia Shipment.loadDemands che Vehicle.loadLimits utilizzano il tipo buffer di protocollo 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 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à e maxLoad rappresenta la capacità di carico massima del veicolo nel tipo specificato.

Il valore loadDemands di una spedizione consuma il valore loadLimits del veicolo assegnato solo se i due hanno chiavi del tipo di carico corrispondenti. Ad esempio, una spedizione con loadDemands di:

"loadDemands": {
  "weightKg": {
    "amount": 50
  }
}

richiede 50 unità di caricamento nel tipo weightKg per completare la spedizione. 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 ai loadDemands della spedizione nel tipo weightKg. Tuttavia, un veicolo con loadLimits di:

"loadLimits": {
  "equipmentRackStorage": {
    "maxLoad": 10
  }
}

ha implicitamente una capacità di weightKg illimitata a causa dell'assenza di un limite di carico di weightKg, quindi il veicolo non è vincolato dalla domanda di peso della spedizione.

Trasferimento del carico tra le spedizioni e i veicoli

Poiché le spedizioni vengono ritirate e consegnate dai veicoli, il loadDemand della spedizione viene trasferito tra la spedizione e il veicolo. Puoi visualizzare i caricamenti del veicolo nella voce del messaggio OptimizeToursResponse (REST, gRPC)routes.transitions per un determinato veicolo. La sequenza è la seguente:

  1. La capacità di carico richiesta è definita per la spedizione come loadDemand.
  2. La spedizione viene ritirata dal veicolo assegnato e il valore vehicleLoads del veicolo aumenta dell'importo di loadDemand della spedizione. Questo trasferimento è rappresentato da un visits.loadDemands positivo nel messaggio di risposta.
  3. Il veicolo consegna la spedizione e il valore vehicleLoads del veicolo diminuisce del valore di loadDemand della spedizione consegnata. Questo trasferimento è rappresentato da visits.loadDemands negativo nel messaggio di risposta.

Il valore vehicleLoads di un veicolo non può superare il valore loadLimits specificato in nessun punto del percorso.

Un esempio completo con richieste e limiti di carico

Visualizza un esempio di richiesta con richieste e limiti di carico

{
  "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 carico:

  • shipments[0] ha una domanda di carico di 50 weightKg.
  • shipments[1] ha una domanda di carico di 10 weightKg.
  • shipments[2] ha una domanda di carico di 80 weightKg.
  • vehicles[0] ha un limite di carico di 100 weightKg.

Visualizza una risposta alla richiesta con richieste e limiti di carico

{
  "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 carico aggiunti influiscono sull'ordine di visits:

  1. shipment[0] è stato ritirato
  2. shipment[1] è stato ritirato
  3. Il numero shipment[0] è stato consegnato
  4. Il numero shipment[1] è stato consegnato
  5. shipment[2] è stato ritirato
  6. Il numero shipment[2] è stato consegnato

Questo ordine indica che il veicolo non può completare tre spedizioni contemporaneamente perché il totale di loadDemands supera i loadLimits del veicolo.

Ogni voce visits include la modifica del carico del veicolo risultante dal completamento della Visit. I valori di carico positivi rappresentano il carico della spedizione, mentre quelli negativi rappresentano lo scarico della spedizione.

Ogni voce transitions include il carico totale del veicolo durante la Transition. Ad esempio, transitions[2] ha un carico weightKg pari a 60, che rappresenta i caricamenti combinati di shipment[0] e shipment[1].

Gli oggetti delle metriche routes[0].metrics e metrics.aggregatedRouteMetrics includono una proprietà maxLoads. Il valore del tipo weightKg è 80 e rappresenta la parte del percorso del veicolo che ha trasportato shipments[2] fino al luogo di consegna.

Vincoli del limite di carico flessibile

Come per le finestre temporali descritte nella sezione Vincoli della finestra dei tempi di ritiro e consegna, i vincoli del limite di carico hanno varianti fisse e rigide. La proprietà maxLoad del messaggio LoadLimit esprime un vincolo rigido: il veicolo non deve mai portare un carico superiore al valore maxLoad nel tipo specificato. Le proprietà softMaxLoad e costPerUnitAboveSoftMax esprimono un vincolo temporaneo: ogni unità che supera softMaxLoad comporta un costo di costPerUnitAboveSoftMax.

I vincoli del limite di carico temporaneo hanno diversi utilizzi, ad esempio:

  • equilibrare le spedizioni tra più veicoli rispetto al numero minimo necessario quando è conveniente
  • esprimere la preferenza del conducente per il numero di articoli che possono ritirare e consegnare comodamente su un determinato percorso
  • caricare veicoli al di sotto della capacità fisica massima per limitare l'usura e ridurre i costi di manutenzione

I vincoli dei limiti di carico hard e soft possono essere utilizzati insieme. Ad esempio, un limite di carico forzato potrebbe esprimere il peso massimo del carico che un veicolo può trasportare in sicurezza o il numero massimo di oggetti che possono trovarsi in un veicolo contemporaneamente, mentre un limite di carico morbido potrebbe essere il peso massimo o il numero di oggetti che imposterebbero la capacità del conducente di inserire tutto nel veicolo.