本指南介绍了 loadDemands
和 loadLimits
以及它们之间的关系。
如自提和送货时间范围限制中所述,OptimizeToursRequest
消息(REST、gRPC)包含许多属性,用于指定针对正在优化的问题的限制条件。多个 OptimizeToursRequest
属性表示加载约束条件。
车辆和货运具有在规划路线时必须考虑的物理属性。
- 车辆:
loadLimits
属性用于指定车辆可以处理的最大负载。请参阅Vehicle
消息的(REST、gRPC)文档。 - 装运:
loadDemands
属性指定给定装运的装运量。请参阅Shipment
消息的(REST、gRPC)文档。
这两个约束条件相结合,可以让优化器以最符合您车队容量和货运需求的方式,将货物分配给车辆。
本文的其余部分详细介绍了 loadLimits
和 loadDemands
。
加载需求和限制:类型
您可以通过类型表示每个加载需求和限制约束。
您可以提供自己的加载类型集,如以下示例所示:
- 权重
- 卷
- 线性测量
- 运输的物品或设备的名称
本指南使用 weightKg
作为示例类型。
Shipment.loadDemands
和 Vehicle.loadLimits
都使用 Protocol Buffers map
类型,其中 string
键表示加载类型。
Shipment.loadDemands
值使用 Load
消息(REST、gRPC)。Load
消息具有单个 amount
属性,该属性表示完成指定类型的装运所需的容量。
Vehicle.loadLimits
值使用 LoadLimit
消息(REST、gRPC)。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
消息(REST、gRPC)routes.transitions
条目中查看车辆的负载。具体顺序如下:
- 所需的承载能力以
loadDemand
的形式定义运单。 - 运单由分配的车辆提货,车辆的
vehicleLoads
增加运单的loadDemand
量。此转移作业在响应消息中由正visits.loadDemands
表示。 - 车辆交付货物,且车辆的
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]
的加载需求为 50weightKg
。shipments[1]
的加载需求为 10weightKg
。shipments[2]
的加载需求为 80weightKg
。vehicles[0]
的加载限制为 100weightKg
。
查看包含负载需求和限制的请求的响应
{ "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
的顺序:
shipment[0]
已上车shipment[1]
已上车shipment[0]
已送达shipment[1]
已送达shipment[2]
已上车shipment[2]
已送达
此订单反映车辆无法同时完成三件装运,因为总 loadDemands
超出了车辆的 loadLimits
。
每个 visits
条目都包含完成 Visit
导致的车辆负载变化。正负值表示货物装运,负值表示货物卸载。
每个 transitions
条目都包含 Transition
期间的车辆总负载。例如,transitions[2]
的 weightKg
负载为 60,表示 shipment[0]
和 shipment[1]
的总负载。
指标对象 routes[0].metrics
和 metrics.aggregatedRouteMetrics
包含一个 maxLoads
属性。类型 weightKg
的值为 80,表示将 shipments[2]
传送到其送货地点的车辆路线的一部分。
软加载限制约束
与自提和送货时间范围限制中所述的时间范围一样,负载限制具有硬变体和软变体。LoadLimit
消息的 maxLoad
属性表示一项硬性约束:车辆装载的负载不得超过指定类型的 maxLoad
值。属性 softMaxLoad
和 costPerUnitAboveSoftMax
表示软性约束,每超出 softMaxLoad
都会产生 costPerUnitAboveSoftMax
费用。
软加载限制有几种用途,例如:
- 在尽量经济且具有成本效益的情况下,在多辆车间均衡运货,
- 表明驾驶员对在特定路线上能够舒适取货和送餐的物品数量偏好
- 使车辆负载低于其最大物理容量,以限制磨损并降低维护成本
硬加载限制约束和软加载限制可以一起使用。例如,硬加载限制可能表示车辆可以安全携带的最大货物重量或一次装在车辆中的物品数量上限,而软加载限制则可能表示驾驶员难以安装车辆的最大重量或物品数量。