加载需求和限制

本指南介绍了 loadDemandsloadLimits,以及它们之间的关系。

上门取件和送货时间范围限制中所述,OptimizeToursRequest 消息 (RESTgRPC) 包含一些用于指定要优化问题的约束条件的属性。多个 OptimizeToursRequest 属性代表加载约束条件

车辆和运输具有物理属性,在规划路线时必须加以考虑。

  • 车辆loadLimits 属性用于指定车辆可处理的最大载荷。请参阅 Vehicle 消息的 (RESTgRPC) 文档。
  • 发货loadDemands 属性指定给定发货的负载量。请参阅 Shipment 消息的 (RESTgRPC) 文档。

这两个约束条件相辅相成,可让优化器以最符合您的车队运力和运输需求的方式,将运输任务适当地分配给车辆。

本文档的其余部分将详细介绍 loadLimitsloadDemands

负载需求和限制:类型

您可以通过类型来表示每个负载需求和限制约束。

您可以提供自己的一组加载类型,如以下示例所示:

  • 重量
  • 音量
  • 线性测量
  • 运输的物品或设备的名称

本指南使用 weightKg 作为示例类型。

Shipment.loadDemandsVehicle.loadLimits 都使用 Protocol Buffers map 类型,其中 string 键表示负载类型。

Shipment.loadDemands 值使用 Load 消息(RESTgRPC)。Load 消息只有一个 amount 属性,表示完成指定类型的运输所需的容量。

Vehicle.loadLimits 值使用 LoadLimit 消息(RESTgRPC)。LoadLimit 消息具有多个属性,其中 maxLoad 表示指定类型的车辆载重量上限。

只有当运输的 loadDemands 与分配给它的车辆的 loadLimits 具有匹配的载荷类型键时,运输的 loadDemands 才会消耗分配给它的车辆的 loadLimits。例如,loadDemands 为以下值的运输:

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

需要 50 个 weightKg 类型的载荷单元才能完成发货。车辆 loadLimits 的以下各项:

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

可以完成运输,因为车辆的 weightKg 类型 maxLoad 大于或等于运输的 weightKg 类型 loadDemands。不过,如果车辆的 loadLimits 为:

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

由于没有 weightKg 载重限制,因此隐式具有无限 weightKg 容量,因此车辆不受运输重量需求的约束。

在运输包裹和车辆之间转移负载

当车辆上门取货并配送运输时,运输的 loadDemand 会在运输和车辆之间传输。您可以在给定车辆的 OptimizeToursResponse 消息(RESTgRPCroutes.transitions条目中查看车辆的载荷。序列如下所示:

  1. 为运输订单定义所需的载重量为 loadDemand
  2. 相应车辆会上门取货,并且车辆的 vehicleLoads 会增加相应运输订单的 loadDemand。此传输在响应消息中由 positive visits.loadDemands 表示。
  3. 车辆配送了运输,车辆的 vehicleLoads 会减少所配送运输的 loadDemand 的数量。此转移在响应消息中由 negative visits.loadDemands 表示。

车辆的 vehicleLoads 在其路线上的任何时间点都不能超过其指定的 loadLimits

包含负载需求和限制的完整示例

查看包含负载需求和限制的请求示例

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

示例请求包含多个与加载相关的参数:

  • shipments[0] 的负载需求为 50 weightKg
  • shipments[1] 的负载需求为 10 weightKg
  • shipments[2] 的负载需求为 80 weightKg
  • vehicles[0] 的加载上限为 100 weightKg

查看包含负载需求和限制的请求响应

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

添加的加载约束条件会影响 visits 的顺序:

  1. shipment[0] 已自提
  2. shipment[1] 已自提
  3. shipment[0] 已送达
  4. shipment[1] 已送达
  5. shipment[2] 已自提
  6. shipment[2] 已送达

此订单反映,由于三个运输的 loadDemands 总和超出了车辆的 loadLimits,因此车辆无法同时完成这三个运输。

每个 visits 条目都包含因完成 Visit 而导致的车辆载荷变化。正载荷值表示装运,而负载荷值表示卸运。

每个 transitions 条目都包含 Transition 期间的总车载量。例如,transitions[2]weightKg 负载为 60,表示 shipment[0]shipment[1] 的总负载。

指标对象 routes[0].metricsmetrics.aggregatedRouteMetrics 包含 maxLoads 属性。类型 weightKg 的值为 80,表示车辆将 shipments[2] 运送到其送货地点的路线部分。

软负载限制

上车点和下车点时间范围限制中所述的时间范围一样,载客量限制也有硬性和软性变体。LoadLimit 消息的 maxLoad 属性表示硬限制:车辆不得携带超出指定类型的 maxLoad 值的载荷。属性 softMaxLoadcostPerUnitAboveSoftMax 表示软约束条件,超出 softMaxLoad 的每个单位都会产生 costPerUnitAboveSoftMax 费用。

软负载限制有几种用途,例如:

  • 在费用效益可观的情况下,将运输任务分配给超过最低必要数量的车辆
  • 表达司机对在给定路线上可以舒适地接收和送达的物品数量的偏好
  • 将车辆载重量控制在其实际载重量的上限以下,以限制磨损并降低维护费用

硬性和软性负载限制可以结合使用。例如,硬载重上限可能表示车辆可以安全运载的货物重量上限或车辆一次可装载的物品数量上限,而软载重上限可能表示会使驾驶员难以将所有物品装载到车辆中的重量上限或物品数量上限。