Exemple : Demandes et limites de charge

Cet exemple montre comment utiliser loadDemands et loadLimits pour gérer les contraintes de capacité des véhicules dans une requête API Route Optimization.

Pour obtenir une présentation conceptuelle complète, consultez le document Concepts clés des demandes et des limites de charge.

Exemple de requête

L'exemple suivant illustre un scénario dans lequel un seul véhicule avec une limite de charge doit livrer trois envois de poids différents.

Cet exemple de requête contient les paramètres de charge suivants :

  • shipments[0] avec une demande de charge amount de 50 weightKg.
  • shipments[1] avec une demande de charge amount de 10 weightKg.
  • shipments[2] avec une demande de charge amount de 80 weightKg.
  • vehicles[0] avec une limite de charge maxLoad de 100 weightKg.

Voir un exemple de requête avec des demandes et des 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"
          }
        }
      }
    ]
  }
}
    

Exemple de réponse

La réponse affiche l'itinéraire optimisé pour le véhicule. Comme la charge totale de tous les envois dépasse la capacité du véhicule, l'optimiseur crée une séquence d'enlèvements et de livraisons pour s'assurer que les loadLimits ne sont pas enfreintes.

Consultez 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
    }
  }
}
    

Étant donné que la valeur loadDemands combinée des trois envois (50 + 10 + 80 = 140) dépasse la valeur loadLimits du véhicule (100), le véhicule ne peut pas récupérer tous les envois en même temps. L'optimiseur ne tient compte que des itinéraires où shipment[0] et shipment[2] ne se trouvent pas dans le véhicule en même temps, car ce sont ces expéditions qui dépassent la limite de charge du véhicule avec leur poids combiné.

L'itinéraire présente les visits suivants pour ne pas dépasser la limite de charge du véhicule :

  1. shipment[0] est récupéré
  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é

La charge du véhicule change tout au long de l'itinéraire, ce que vous pouvez observer dans le tableau transitions. Par exemple, transitions[2] indique que le véhicule transporte une charge de 60 weightKg après avoir récupéré les deux premiers envois (50 + 10).

La propriété maxLoads dans metrics indique que la charge maximale transportée à n'importe quel point de l'itinéraire était de 80 weightKg, ce qui confirme que la solution est restée dans la limite de 100 weightKg du véhicule.

Limites de charge souples

L'exemple suivant montre comment utiliser une limite de charge souple pour optimiser un itinéraire avec plusieurs véhicules. La solution répartit les expéditions entre les deux véhicules pour éviter la pénalité de coût liée au dépassement de la limite de charge souple du véhicule.

Exemple de requête

Cette requête inclut désormais trois envois de livraison uniquement et deux véhicules avec les mêmes loadLimits et softMaxLoad.

Les paramètres clés de cet exemple sont les suivants :

Voir un exemple de requête avec une limite de charge souple

{
  "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"
          }
        ],
        
        "loadDemands": {
          "weightKg": {
            "amount": "50"
          }
        }
        
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 37.789116,
              "longitude": -122.395080
            },
            "duration": "250s"
          }
        ],
        
        "loadDemands": {
          "weightKg": {
            "amount": "60"
          }
        }
        
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 37.795242,
              "longitude": -122.399347
            },
            "duration": "250s"
          }
        ],
        
        "loadDemands": {
          "weightKg": {
            "amount": "30"
          }
        }
        
      }
    ],
    "vehicles": [
      {
        "endLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "startLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "costPerHour": 40.0,
        "costPerKilometer": 10.0,
        
        "loadLimits": {
          "weightKg": {
            "maxLoad": "150",
            "softMaxLoad": "100",
            "costPerUnitAboveSoftMax": 5.0
          }
        }
        
      },
      {
        "endLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "startLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "costPerHour": 40.0,
        "costPerKilometer": 10.0,
        
        "loadLimits": {
          "weightKg": {
            "maxLoad": "150",
            "softMaxLoad": "100",
            "costPerUnitAboveSoftMax": 5.0
          }
        }
        
      }
    ]
  }
}
    

Exemple de réponse

La réponse contient désormais deux itinéraires, un pour chaque véhicule. L'optimiseur détermine que l'utilisation des deux véhicules est plus rentable que l'utilisation d'un seul véhicule et l'encours de la pénalité de limite souple.

Voir une réponse à la requête avec une limite de charge souple

{
  "routes": [
    {
      "vehicleStartTime": "2023-01-13T16:00:00Z",
      "vehicleEndTime": "2023-01-13T16:13:31Z",
      "visits": [
        {
          "startTime": "2023-01-13T16:03:53Z",
          "detour": "0s",
          "loadDemands": {
            "weightKg": {
              "amount": "-50"
            }
          }
        }
      ],
      "transitions": [
        {
          "travelDuration": "233s",
          "travelDistanceMeters": 794,
          "waitDuration": "0s",
          "totalDuration": "233s",
          "startTime": "2023-01-13T16:00:00Z",
          "vehicleLoads": {
            "weightKg": {
              "amount": "50"
            }
          }
        },
        {
          "travelDuration": "328s",
          "travelDistanceMeters": 1188,
          "waitDuration": "0s",
          "totalDuration": "328s",
          "startTime": "2023-01-13T16:08:03Z",
          "vehicleLoads": {
            "weightKg": {}
          }
        }
      ],
      "metrics": {
        "performedShipmentCount": 1,
        "travelDuration": "561s",
        "visitDuration": "250s",
        "totalDuration": "811s",
        "travelDistanceMeters": 1982,
        "maxLoads": {
          "weightKg": {
            "amount": "50"
          }
        }
      },
      "routeCosts": {
        "model.vehicles.cost_per_kilometer": 19.82,
        "model.vehicles.cost_per_hour": 9.01
      },
      "routeTotalCost": 28.83
    },
    {
      "vehicleIndex": 1,
      "vehicleStartTime": "2023-01-13T16:00:00Z",
      "vehicleEndTime": "2023-01-13T16:21:43Z",
      "visits": [
        {
          "shipmentIndex": 1,
          "startTime": "2023-01-13T16:05:54Z",
          "detour": "0s",
          "loadDemands": {
            "weightKg": {
              "amount": "-60"
            }
          }
        },
        {
          "shipmentIndex": 2,
          "startTime": "2023-01-13T16:13:52Z",
          "detour": "473s",
          "loadDemands": {
            "weightKg": {
              "amount": "-30"
            }
          }
        }
      ],
      "transitions": [
        {
          "travelDuration": "354s",
          "travelDistanceMeters": 1196,
          "waitDuration": "0s",
          "totalDuration": "354s",
          "startTime": "2023-01-13T16:00:00Z",
          "vehicleLoads": {
            "weightKg": {
              "amount": "90"
            }
          }
        },
        {
          "travelDuration": "228s",
          "travelDistanceMeters": 808,
          "waitDuration": "0s",
          "totalDuration": "228s",
          "startTime": "2023-01-13T16:10:04Z",
          "vehicleLoads": {
            "weightKg": {
              "amount": "30"
            }
          }
        },
        {
          "travelDuration": "221s",
          "travelDistanceMeters": 655,
          "waitDuration": "0s",
          "totalDuration": "221s",
          "startTime": "2023-01-13T16:18:02Z",
          "vehicleLoads": {
            "weightKg": {}
          }
        }
      ],
      "metrics": {
        "performedShipmentCount": 2,
        "travelDuration": "803s",
        "visitDuration": "500s",
        "totalDuration": "1303s",
        "travelDistanceMeters": 2659,
        "maxLoads": {
          "weightKg": {
            "amount": "90"
          }
        }
      },
      "routeCosts": {
        "model.vehicles.cost_per_kilometer": 26.59,
        "model.vehicles.cost_per_hour": 14.48
      },
      "routeTotalCost": 41.07
    }
  ],
  "metrics": {
    "aggregatedRouteMetrics": {
      "performedShipmentCount": 3,
      "travelDuration": "1364s",
      "visitDuration": "750s",
      "totalDuration": "2114s",
      "travelDistanceMeters": 4641,
      "maxLoads": {
        "weightKg": {
          "amount": "90"
        }
      }
    },
    "usedVehicleCount": 2,
    "earliestVehicleStartTime": "2023-01-13T16:00:00Z",
    "latestVehicleEndTime": "2023-01-13T16:21:43Z",
    "totalCost": 69.90,
    "costs": {
      "model.vehicles.cost_per_kilometer": 46.41,
      "model.vehicles.cost_per_hour": 23.49
    }
  }
}
    

Les champs suivants montrent comment l'optimiseur a réparti les expéditions entre les deux véhicules pour que les charges restent inférieures à la limite flexible de 100 weightKg.

  • La première route (vehicleIndex : 0) gère l'expédition de 50 weightKg. Son maxLoads est défini sur "50", ce qui est inférieur à la limite flexible.
  • La deuxième route (vehicleIndex : 1) gère les 60 et 30 envois weightKg. Son maxLoads est de "90", ce qui est également inférieur à la limite souple.
  • Comme aucun véhicule ne dépasse sa limite souple, le routeCosts des deux itinéraires n'affiche aucune pénalité costPerUnitAboveSoftMax.