このガイドでは、loadDemands
と loadLimits
の概要と、これらの関係について説明します。
集荷と配達の時間帯の制約で説明したように、OptimizeToursRequest
メッセージ(REST、gRPC)には、最適化される問題の制約を指定する多くのプロパティが含まれています。いくつかの OptimizeToursRequest
プロパティは負荷の制約を表します。
車両と荷物には物理的な特性があり、ルート計画の際に考慮する必要があります。
- 車両:
loadLimits
プロパティは、車両が処理できる最大負荷を指定します。Vehicle
メッセージ(REST、gRPC)のドキュメントをご覧ください。 - Shipments:
loadDemands
プロパティは、特定の送信で消費される負荷の量を指定します。Shipment
メッセージ(REST、gRPC)のドキュメントをご覧ください。
これら 2 つの制約により、オプティマイザーはフリート容量と出荷需要に最適な方法で、車両に出荷を適切に割り当てることができます。
このドキュメントの残りの部分では、loadLimits
と loadDemands
について詳しく説明します。
負荷需要と上限: タイプ
各負荷需要と上限制約は、タイプで表します。
次の例のように、独自の読み込みタイプのセットを指定できます。
- weight
- 音量
- 線形測定
- 運搬する物品や機器の名前
このガイドでは、weightKg
をタイプとして使用します。
Shipment.loadDemands
と Vehicle.loadLimits
の両方が Protocol Buffers map
型を使用し、負荷のタイプを表す string
キーを使用します。
Shipment.loadDemands
値は Load
メッセージ(REST、gRPC)を使用します。Load
メッセージには、指定されたタイプの配送を完了するために必要な容量を表す単一の amount
プロパティがあります。
Vehicle.loadLimits
値は LoadLimit
メッセージ(REST、gRPC)を使用します。LoadLimit
メッセージには複数のプロパティがあり、maxLoad
は指定されたタイプにおける車両の最大積載容量を表します。
配送の loadDemands
は、2 つの車両の積載タイプのキーが一致する場合にのみ、割り当てられた車両の loadLimits
を消費します。たとえば、loadDemands
が次のような配送の場合:
"loadDemands": {
"weightKg": {
"amount": 50
}
}
配送を完了するには、weightKg
タイプの荷物単位が 50 個必要です。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
を超えているため、車両で 3 つの出荷を同時に完了できないことを示しています。
各 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
コストが発生します。
ソフトロード制限の制約には、次のような用途があります。
- 費用対効果が高い場合は、必要な最小数よりも多くの車両に配送を分散する
- 特定のルートで快適に乗車および配達できる アイテムの数をドライバーの好みを表す
- 車両に積載可能な最大物理的容量を下回って摩耗を抑え、メンテナンス費用を削減
ハード負荷制限とソフト負荷制限は併用できます。たとえば、ハード積載制限は、車両が安全に運ぶことができる貨物の最大重量や 1 車両に一度に収納できるアイテムの最大数を表します。一方、ソフト積載制限は、ドライバーが車両にすべて収納する能力に負担がかかる最大重量またはアイテム数を表します。