本指南介绍了 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 具有匹配的装载类型键时,货件才会消耗车辆的 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] 的负载为 60,表示 shipment[0] 和 shipment[1] 的总负载。weightKg
指标对象 routes[0].metrics 和 metrics.aggregatedRouteMetrics 包含 maxLoads 属性。类型为 weightKg 的值为 80,表示车辆运输 shipments[2] 到其送货地点的路线所占的比例。
软负载限制约束
与取货和送货时间窗口限制中所述的时间窗口一样,装载量限制也分为硬性限制和软性限制。LoadLimit 消息的 maxLoad 属性表示一种硬约束:车辆的载荷绝不能超过指定类型的 maxLoad 值。属性 softMaxLoad 和 costPerUnitAboveSoftMax 表示一种软约束,每超出 softMaxLoad 一个单位就会产生 costPerUnitAboveSoftMax 的费用。
软负载限制约束有多种用途,例如:
- 在这样做具有成本效益时,将货物分配到比必要最少数量更多的车辆上
- 表达司机对在给定路线上可以舒适地取件和送件的数量的偏好
- 使车辆的装载量低于其最大物理容量,以限制磨损并降低维护成本
硬性和软性负载限制约束可以一起使用。例如,硬性载重限制可能表示车辆可以安全载运的最大货物重量,或者车辆一次最多可容纳的物品数量,而软性载重限制可能表示会影响驾驶员将所有物品装入车辆的能力的最大重量或物品数量。