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 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 chargement 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 de retraits et de livraisons pour s'assurer que les loadLimits ne sont pas enfreintes.

Consulter 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 prend en compte que les itinéraires où shipment[0] et shipment[2] ne se trouvent pas dans le véhicule en même temps, car ces envois dépassent la limite de charge du véhicule avec leurs poids combinés.

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

  1. shipment[0] est décroché
  2. shipment[1] est décroché
  3. shipment[0] est livré
  4. shipment[1] est livré
  5. shipment[2] est décroché
  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 envois 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'application de la pénalité de limite souple.

Consultez 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 envois entre les deux véhicules pour maintenir les charges en dessous de la limite souple de 100 weightKg.

  • Le premier itinéraire (vehicleIndex : 0) gère l'envoi de 50 weightKg. Son maxLoads est de 50, ce qui est inférieur à la limite souple.
  • La deuxième route (vehicleIndex : 1) gère les expéditions 60 et 30 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, les routeCosts des deux itinéraires n'affichent aucune pénalité costPerUnitAboveSoftMax.