במדריך הזה נסביר על loadDemands
ועל loadLimits
, ואיך הם קשורים זה לזה.
כמו שצוין במאמר מגבלות על חלונות זמן לאיסוף ולמשלוח, ההודעה OptimizeToursRequest
(REST, gRPC) מכילה מספר מאפיינים שמציינים מגבלות על הבעיה שעוברת אופטימיזציה. כמה מאפיינים של OptimizeToursRequest
מייצגים מגבלות עומס.
לכלי רכב ולמשלוחים יש מאפיינים פיזיים שצריך לקחת בחשבון כשמתכננים מסלול.
- כלי רכב: ב
loadLimits
מאפייני הנכס מצוין העומס המקסימלי שכלי הרכב יכול לשאת. מידע נוסף זמין במסמכי התיעוד של ההודעהVehicle
(REST, gRPC). - Shipments: המאפיין
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
}
}
יכול להיות שניתן יהיה להשלים את המשלוח, כי ערך maxLoad
של הרכב בסוג weightKg
גדול או שווה לערך loadDemands
של המשלוח בסוג weightKg
. עם זאת, רכב עם loadLimits
של:
"loadLimits": {
"equipmentRackStorage": {
"maxLoad": 10
}
}
יש לו קיבולת של weightKg
באופן מרומז כי אין לו מגבלת עומס של weightKg
, ולכן הרכב לא מוגבל לפי משקל המשלוח.
העברת מטען בין משלוחים וכלי רכב
כשהמשלוחים נאספים ומועברים על ידי כלי רכב, נתוני המשלוח loadDemand
מועברים בין המשלוח לכלי הרכב. אפשר לראות את המטען של הרכב ברשומה OptimizeToursResponse
של ההודעה (REST, gRPC)routes.transitions
של רכב מסוים. הרצף הוא כדלקמן:
- קיבולת העומס הנדרשת מוגדרת למשלוח כ-
loadDemand
. - המשלוח נאסף על ידי הרכב שהוקצה לו, והערך של
vehicleLoads
של הרכב גדל בכמות שלloadDemand
של המשלוח. ההעברה הזו מיוצגת על ידי positivevisits.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
, שמייצג את העומסים המשולבים של shipment[0]
ו-shipment[1]
.
אובייקטים של מדדים routes[0].metrics
ו-metrics.aggregatedRouteMetrics
כוללים מאפיין maxLoads
. הערך של סוג weightKg
הוא 80, שמייצג את החלק ממסלול הנסיעה של הרכב שבו הועבר shipments[2]
למיקום המסירה שלו.
אילוצים של מגבלת טעינה רכה
בדומה לחלונות הזמן שמתוארים במאמר מגבלות על חלונות זמן לאיסוף ולמשלוח, למגבלות על קיבולת העמסה יש גרסאות מחמירות וגרסאות מקלות. המאפיין maxLoad
של ההודעה LoadLimit
מבטא אילוץ קשיח: אסור שהמטען של הרכב יעלה על הערך maxLoad
בסוג שצוין. המאפיינים softMaxLoad
ו-costPerUnitAboveSoftMax
מייצגים אילוץ רך, שבו כל יחידה מעל softMaxLoad
גוררת עלות של costPerUnitAboveSoftMax
.
למגבלות על עומס רך יש כמה שימושים, למשל:
- חלוקת המשלוחים בין יותר כלי רכב מהמספר המינימלי הנדרש, אם זה משתלם מבחינת עלות
- העדפת הנהג לגבי מספר הפריטים שהוא יכול לאסוף ולמסור בנוחות במסלול נתון
- טעינת כלי רכב מתחת לקיבולת הפיזית המקסימלית שלהם כדי להגביל את הבלאי ולהפחית את עלויות התחזוקה
אפשר להשתמש ביחד באילוצים של מגבלת טעינה קשיחה ומגבלת טעינה רכה. לדוגמה, מגבלת עומס קשיחה יכולה להיות המשקל המקסימלי של המטען שכלי רכב יכול לשאת בבטחה או מספר הפריטים המקסימלי שיכולים להיכנס לכלי רכב בכל פעם, בעוד שמגבלת עומס רכה יכולה להיות המשקל המקסימלי או מספר הפריטים שיקשו על הנהג להכניס את הכול לכלי הרכב.