Demandes et limites de charge

Ce guide décrit loadDemands et loadLimits, ainsi que leurs relations.

Comme indiqué dans la section Contraintes de délai de prise en charge et de livraison, le message OptimizeToursRequest (REST, gRPC) contient un certain nombre de propriétés qui spécifient des contraintes concernant le problème à optimiser. Plusieurs propriétés OptimizeToursRequest représentent des contraintes de chargement.

Les véhicules et les livraisons possèdent des propriétés physiques qui doivent être prises en compte lors de la planification d'un itinéraire.

  • Véhicules: la propriété loadLimits spécifie la charge maximale que le véhicule peut supporter. Consultez la documentation du message Vehicle (REST, gRPC).
  • Expéditions: la propriété loadDemands spécifie la charge consommée par un envoi donné. Consultez la documentation du message Shipment (REST, gRPC).

Ensemble, ces deux contraintes permettent à l'optimiseur d'attribuer les expéditions aux véhicules de la manière la plus adaptée à la capacité de votre parc et aux besoins en livraison.

Ce reste de ce document traite de loadLimits et de loadDemands en détail.

Exigences et limites de charge: types

Vous exprimez chaque demande de charge et contrainte de limite en termes de type.

Vous pouvez fournir votre propre ensemble de types de chargement, comme dans les exemples suivants:

  • pondération
  • volume
  • mesures linéaires
  • noms des objets ou équipements transportés

Ce guide utilise weightKg comme exemple de type.

Shipment.loadDemands et Vehicle.loadLimits utilisent le type map de Protocol Buffers, avec des clés string qui représentent les types de charge.

Les valeurs Shipment.loadDemands utilisent le message Load (REST, gRPC). Le message Load comporte une seule propriété amount représentant la capacité requise pour finaliser la livraison dans le type spécifié.

Les valeurs Vehicle.loadLimits utilisent le message LoadLimit (REST, gRPC). Le message LoadLimit comporte plusieurs propriétés, maxLoad représentant la capacité de charge maximale du véhicule pour le type spécifié.

L'loadDemands d'une livraison ne consomme l'loadLimits du véhicule qui lui a été attribué que si les deux ont des clés de type de chargement correspondantes. Par exemple, un envoi avec loadDemands de:

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

nécessite 50 unités de chargement de type weightKg pour que la livraison soit terminée. Un véhicule avec un loadLimits de:

"loadLimits": {
  "weightKg": {
    "maxLoad": 100
  }
}

peut être en mesure de terminer la livraison, car la valeur maxLoad du véhicule dans le type weightKg est supérieure ou égale à la valeur loadDemands du véhicule dans le type weightKg. Toutefois, un véhicule avec loadLimits:

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

dispose implicitement d'une capacité weightKg illimitée en raison de l'absence de limite de charge weightKg, de sorte que le véhicule n'est pas limité par la demande de poids de la livraison.

Transfert de charge entre les expéditions et les véhicules

Lorsque les colis sont récupérés et livrés par des véhicules, les valeurs loadDemand de la livraison sont transférées entre la livraison et le véhicule. Vous pouvez voir les chargements du véhicule dans l'entrée du message OptimizeToursResponse (REST, gRPC)routes.transitions pour un véhicule donné. La séquence est la suivante:

  1. La capacité de charge requise pour le colis est définie en tant que loadDemand.
  2. Le colis est retiré par le véhicule qui lui a été attribué, et le vehicleLoads du véhicule augmente du montant du loadDemand du colis. Ce transfert est représenté par une valeur visits.loadDemands positive dans le message de réponse.
  3. Le véhicule livre le colis, et le vehicleLoads du véhicule diminue du montant de l'loadDemand du colis livré. Ce transfert est représenté par une valeur visits.loadDemands négative dans le message de réponse.

La valeur vehicleLoads d'un véhicule ne peut pas dépasser sa valeur loadLimits spécifiée à aucun moment de l'itinéraire.

Exemple complet présentant les exigences et les limites de charge

Voir un exemple de requête avec les exigences et les limites de charge

{
  "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
          }
        }
      }
    ]
  }
}
    

L'exemple de requête contient plusieurs paramètres liés au chargement:

  • shipments[0] a une demande de charge de 50 weightKg.
  • shipments[1] a une demande de charge de 10 weightKg.
  • shipments[2] a une demande de charge de 80 weightKg.
  • La limite de chargement de vehicles[0] est de 100 weightKg.

Affichez une réponse à la requête avec les demandes et les limites de charge.

{
  "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
    }
  }
}
    

Les contraintes de charge ajoutées affectent l'ordre de visits:

  1. shipment[0] a été retiré
  2. shipment[1] a été retiré
  3. shipment[0] est livré
  4. shipment[1] est livré
  5. shipment[2] a été retiré
  6. shipment[2] est livré

Cette commande indique que le véhicule ne peut pas effectuer trois expéditions en même temps, car leur loadDemands total dépasse la valeur loadLimits du véhicule.

Chaque entrée visits inclut la modification de la charge du véhicule résultant de l'achèvement de Visit. Les valeurs de chargement positives représentent le chargement du colis, tandis que les valeurs négatives indiquent le déchargement du colis.

Chaque entrée transitions inclut la charge totale du véhicule pendant le Transition. transitions[2], par exemple, présente une charge weightKg de 60, représentant les charges combinées de shipment[0] et shipment[1].

Les objets de métriques routes[0].metrics et metrics.aggregatedRouteMetrics incluent une propriété maxLoads. La valeur du type weightKg est de 80, ce qui correspond à la partie de l'itinéraire du véhicule qui a transporté shipments[2] jusqu'au lieu de livraison.

Contraintes de limite de chargement partiel

Comme pour les fenêtres temporelles décrites dans la section Contraintes liées aux périodes de retrait et de livraison, les contraintes de limite de charge ont des variantes strictes et souples. La propriété maxLoad du message LoadLimit exprime une contrainte stricte: le véhicule ne doit jamais transporter une charge qui dépasse la valeur maxLoad du type spécifié. Les propriétés softMaxLoad et costPerUnitAboveSoftMax expriment une contrainte flexible : chaque unité dépassant softMaxLoad entraîne un coût de costPerUnitAboveSoftMax.

Les contraintes de limite de chargement partiel ont plusieurs utilisations, par exemple:

  • en équilibrant les expéditions sur plus de véhicules que le nombre minimum requis lorsque cela est rentable
  • en exprimant la préférence du conducteur concernant le nombre d'articles qu'il peut facilement récupérer et livrer sur un itinéraire donné.
  • charger des véhicules en dessous de sa capacité physique maximale pour limiter l'usure et réduire les coûts de maintenance

Les contraintes de limite de chargement fixe et de limite de charge flexible peuvent être utilisées ensemble. Par exemple, une limite de charge élevée peut exprimer le poids maximal de la cargaison qu'un véhicule peut transporter en toute sécurité ou le nombre maximal d'objets qu'un véhicule peut transporter en même temps, tandis qu'une limite de charge partielle peut correspondre au poids ou au nombre maximal d'éléments qui limiteraient la capacité du conducteur à tout transporter.