加载需求和限制

本指南介绍了 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 为:

"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 量。此转移作业在响应消息中由 visits.loadDemands 表示。
  3. 车辆交付货物,且车辆的 vehicleLoads 降低已交付货物的 loadDemand 量。此转移作业在响应消息中以 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 费用。

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

  • 在尽量经济且具有成本效益的情况下,在多辆车间均衡运货,
  • 表明驾驶员对在特定路线上能够舒适取货和送餐的物品数量偏好
  • 使车辆负载低于其最大物理容量,以限制磨损并降低维护成本

硬加载限制约束和软加载限制可以一起使用。例如,硬加载限制可能表示车辆可以安全携带的最大货物重量或一次装在车辆中的物品数量上限,而软加载限制则可能表示驾驶员难以安装车辆的最大重量或物品数量。