Demandes et limites de charge

Ce guide décrit loadDemands et loadLimits, ainsi que leur relation.

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 charge.

Les véhicules et les expéditions ont 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 correctement les envois aux véhicules de manière à correspondre au mieux à la capacité de votre flotte et aux demandes d'expédition.

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

Demandes 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 charge, comme les exemples suivants:

  • weight
  • volume
  • mesures linéaires
  • noms des articles ou équipements transportés

Ce guide utilise weightKg comme exemple de type.

Shipment.loadDemands et Vehicle.loadLimits utilisent le type map des tampons de protocole, 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 terminer 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 dans le type spécifié.

Le loadDemands d'une expédition ne consomme le loadLimits du véhicule qui lui est attribué que si les deux ont des clés de type de charge correspondantes. Par exemple, un envoi avec loadDemands de:

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

nécessite 50 unités de charge du type weightKg pour que l'expédition soit effectuée. Véhicule avec loadLimits:

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

peut effectuer l'envoi, car la valeur maxLoad du véhicule dans le type weightKg est supérieure ou égale à la valeur loadDemands de l'envoi dans le type weightKg. Toutefois, un véhicule avec loadLimits:

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

La capacité weightKg est illimitée en raison de l'absence de limite de charge weightKg. Le véhicule n'est donc pas limité par la demande de poids de l'envoi.

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

Lorsque les envois sont récupérés et livrés par des véhicules, l'loadDemand de l'envoi est transféré entre l'envoi 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 sur 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 un visits.loadDemands positif dans le message de réponse.
  3. Le véhicule livre l'envoi et la vehicleLoads du véhicule diminue du montant de la loadDemand de l'envoi livré. Ce transfert est représenté par un visits.loadDemands négatif dans le message de réponse.

Le vehicleLoads d'un véhicule ne peut pas dépasser son loadLimits spécifié à tout moment de son parcours.

Exemple complet avec demandes et 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 à la charge:

  • 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 chargement ajoutées affectent l'ordre des visits:

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

Cette commande indique que trois envois ne peuvent pas être effectués par le véhicule en même temps, car leur loadDemands total dépasse la loadLimits du véhicule.

Chaque entrée visits inclut la modification de la charge du véhicule résultant de l'achèvement de la 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 80, ce qui représente la partie du trajet du véhicule qui a transporté shipments[2] jusqu'à son lieu de livraison.

Contraintes de limite de charge souple

Comme pour les périodes décrites dans la section Contraintes de période de ramassage et de livraison, les contraintes de limite de charge comportent des variantes strictes et flexibles. La propriété maxLoad du message LoadLimit exprime une contrainte stricte: le véhicule ne doit jamais transporter de charge dépassant la valeur maxLoad du type spécifié. Les propriétés softMaxLoad et costPerUnitAboveSoftMax expriment une contrainte souple, chaque unité dépassant softMaxLoad entraînant un coût costPerUnitAboveSoftMax.

Les contraintes de limite de charge souple ont plusieurs utilisations, par exemple:

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

Vous pouvez utiliser des contraintes de limite de charge stricte et flexible. Par exemple, une limite de charge stricte peut indiquer le poids maximal de la cargaison qu'un véhicule peut transporter en toute sécurité ou le nombre maximal d'articles pouvant être placés dans un véhicule à la fois, tandis qu'une limite de charge souple peut être le poids ou le nombre maximal d'articles qui mettrait à rude épreuve la capacité du conducteur à tout faire tenir dans le véhicule.