本指南介绍了 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
才会消耗分配给它的车辆的 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
。此传输在响应消息中由 positivevisits.loadDemands
表示。 - 车辆配送了运输,车辆的
vehicleLoads
会减少所配送运输的loadDemand
的数量。此转移在响应消息中由 negativevisits.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
费用。
软负载限制有多个用途,例如:
- 在费用效益可观的情况下,将运输任务分配给超过最低必要数量的车辆
- 表达司机对在给定路线上可以舒适地接收和送达的物品数量的偏好
- 将车辆载重量控制在其实际载重量的上限以下,以限制磨损并降低维护费用
硬性和软性负载限制可以结合使用。例如,硬负载限制可以表示车辆可安全携带的货物的最大重量或一次可装在一辆车中的最大物品数量,而软负载限制可以表示对驾驶员在装配车辆中的所有物品造成影响的最大重量或物品数量。