OptimizeToursRequest
מחילה מגבלות על:
- משלוחים, שמשפיעים על אופן ביצוע המשלוחים
- כלי רכב, שמשפיעים על אופן החישוב של מסלולי כלי רכב
- השפעה גלובלית על כלי רכב ועל משלוחים.
המדריך הזה מתמקד במגבלות משלוח חיוניות: חלונות זמן.
חלונות זמן הם סוג האילוץ שאתם מספקים בקובץ
הודעה אחת (OptimizeToursRequest
) (REST, gRPC) כדי לציין
מגבלות מבוססות-זמן על פעילויות המשלוח. סוג האילוץ הזה משפיע
גם הזמן והאופן שבהם ניתן לבצע את המשלוח וגם את הקצאת הרכב
למשלוח. עם מגבלות אלה, כלי האופטימיזציה מעניק עדיפות
כלי הרכב שיכולים לעמוד בצורה הטובה ביותר במגבלות הזמן של המשלוח.
מגבלות משלוח: חלונות זמן
מציינים מתי אפשר לאסוף או לקבל משלוח בתוך Shipment.VisitRequest
הודעה כזו:
- שימוש במאפיין
timeWindows
בהודעה (REST, gRPC) - צריך לציין את שעת ההתחלה ושעת הסיום בהודעה של
TimeWindow
(REST, gRPC).
דוגמה לבקשה עם מגבלות של חלון זמן
הדוגמה כאן ממחישה שלושה משלוחים שונים, לכל אחד מהם
חלון הזמנים למשלוח. כדי לשמור על הסבר פשוט, בדוגמה הזו מגדירים חלונות זמן ב-deliveries
בלבד, אבל אפשר להגדיר חלון זמן גם לאיסוף עצמי. אפשר להגדיר כמה חלונות זמן
למרות שהדוגמה הזו משתמשת בערך אחד לכל משלוח VisitRequest
.
הצגת בקשה לדוגמה עם חלונות זמן
{ "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", "timeWindows": [ { "startTime": "2023-01-13T18:00:00Z", "endTime": "2023-01-13T19:00:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 100.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.789116, "longitude": -122.395080 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T18:00:00Z", "endTime": "2023-01-13T18:30:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 20.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.795242, "longitude": -122.399347 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T17:30:00Z", "endTime": "2023-01-13T18:00:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 50.0 } ], "vehicles": [ { "endLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "startLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "costPerHour": 40.0, "costPerKilometer": 10.0 } ] } }
דוגמה לתגובה עם מגבלות של חלון זמן
בתגובה לדוגמה, זמני ההתחלה והסיום של הרכב הם 17:35:50.
18:17:24 בהתאמה. הזמנים האלה משקפים את האופטימיזציה שממזערת את הזמן
נדרש להפעלת הרכב שצוין בבקשה בתור costPerHour
בזמן
עמידה בכל המגבלות של חלון הזמן. שימוש ב-17:35:50 כשעת התחלה
מבטל את הצורך שהרכב צריך להמתין במיקום של ביקור עד
חלון הזמן של הביקור מתחיל. הערך הזה מופיע בתשובה כאפס waitDuration
ערכים.
הצגת תשובה לבקשה לדוגמה עם חלונות זמן
{ "routes": [ { "vehicleStartTime": "2023-01-13T17:35:50Z", "vehicleEndTime": "2023-01-13T18:17:24Z", "visits": [ { "isPickup": true, "startTime": "2023-01-13T17:35:50Z", "detour": "0s" }, { "shipmentIndex": 1, "isPickup": true, "startTime": "2023-01-13T17:38:20Z", "detour": "150s" }, { "shipmentIndex": 2, "isPickup": true, "startTime": "2023-01-13T17:40:50Z", "detour": "300s" }, { "shipmentIndex": 2, "startTime": "2023-01-13T17:50:09Z", "detour": "0s" }, { "shipmentIndex": 1, "startTime": "2023-01-13T18:00:00Z", "detour": "796s" }, { "startTime": "2023-01-13T18:07:35Z", "detour": "1520s" } ], "transitions": [ { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:35:50Z" }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:38:20Z" }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:40:50Z" }, { "travelDuration": "409s", "travelDistanceMeters": 1371, "waitDuration": "0s", "totalDuration": "409s", "startTime": "2023-01-13T17:43:20Z" }, { "travelDuration": "341s", "travelDistanceMeters": 1312, "waitDuration": "0s", "totalDuration": "341s", "startTime": "2023-01-13T17:54:19Z" }, { "travelDuration": "205s", "travelDistanceMeters": 636, "waitDuration": "0s", "totalDuration": "205s", "startTime": "2023-01-13T18:04:10Z" }, { "travelDuration": "339s", "travelDistanceMeters": 1276, "waitDuration": "0s", "totalDuration": "339s", "startTime": "2023-01-13T18:11:45Z" } ], "metrics": { "performedShipmentCount": 3, "travelDuration": "1294s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2494s", "travelDistanceMeters": 4595 }, "routeCosts": { "model.vehicles.cost_per_hour": 27.711111111111112, "model.vehicles.cost_per_kilometer": 45.95 }, "routeTotalCost": 73.661111111111111 } ], "metrics": { "aggregatedRouteMetrics": { "performedShipmentCount": 3, "travelDuration": "1294s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2494s", "travelDistanceMeters": 4595 }, "usedVehicleCount": 1, "earliestVehicleStartTime": "2023-01-13T17:35:50Z", "latestVehicleEndTime": "2023-01-13T18:17:24Z", "totalCost": 73.661111111111111, "costs": { "model.vehicles.cost_per_hour": 27.711111111111112, "model.vehicles.cost_per_kilometer": 45.95 } } }
חלונות זמן הזמינו את visits
של הרכב כך שהמשלוחים עם
חלונות הזמן המוקדם ביותר מוצגים ראשונים.
shipments[2]
נמסר בשעה 17:50- מסירת
shipments[1]
בשעה 18:00 shipments[0]
נמסר בשעה 18:07
הבקשה לדוגמה מציינת מגבלות קשות של חלון זמן,
כדי להשלים את המסירה בחלונות האלה. אם אתם משלימים את פרטי המשלוח
לא ניתן לבצע את VisitRequests
בכל חלונות הזמן שלו, או
משתלם, האופטימיזציה מדלגת על המשלוח. אם המשלוח כולל
penaltyCost
, כלי האופטימיזציה מוסיף אותו לעלויות המדווחות בתגובה
metrics
. אחרת, המאפיין skippedMandatoryShipmentCount
של
הודעה ב-OptimizeToursResponse
(REST, gRPC) עולה.
אם משנים את חלונות הזמן על ידי הזזת החלון של shipment[1]
מספר שעות
לאחר מכן (לשעה 21:00 מ-18:00), התוצאות יהיו שונות כפי שמתואר
בדוגמאות הבאות.
הצגת בקשה לדוגמה עם חלונות זמן שבהם אי אפשר להשתמש להיות מרוצה
{ "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", "timeWindows": [ { "startTime": "2023-01-13T18:00:00Z", "endTime": "2023-01-13T19:00:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 100.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.789116, "longitude": -122.395080 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T21:00:00Z", "endTime": "2023-01-13T21:30:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 20.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.795242, "longitude": -122.399347 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T17:30:00Z", "endTime": "2023-01-13T18:00:00Z" } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 50.0 } ], "vehicles": [ { "endLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "startLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "costPerHour": 40.0, "costPerKilometer": 10.0 } ] } }
הצגת תשובה לבקשה השנייה לדוגמה עם חלונות זמן, שבהם מדלגים על משלוח
{ "routes": [ { "vehicleStartTime": "2023-01-13T17:37:49Z", "vehicleEndTime": "2023-01-13T18:09:49Z", "visits": [ { "isPickup": true, "startTime": "2023-01-13T17:37:49Z", "detour": "0s" }, { "shipmentIndex": 2, "isPickup": true, "startTime": "2023-01-13T17:40:19Z", "detour": "150s" }, { "shipmentIndex": 2, "startTime": "2023-01-13T17:49:38Z", "detour": "0s" }, { "startTime": "2023-01-13T18:00:00Z", "detour": "946s" } ], "transitions": [ { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:37:49Z" }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:40:19Z" }, { "travelDuration": "409s", "travelDistanceMeters": 1371, "waitDuration": "0s", "totalDuration": "409s", "startTime": "2023-01-13T17:42:49Z" }, { "travelDuration": "372s", "travelDistanceMeters": 1348, "waitDuration": "0s", "totalDuration": "372s", "startTime": "2023-01-13T17:53:48Z" }, { "travelDuration": "339s", "travelDistanceMeters": 1276, "waitDuration": "0s", "totalDuration": "339s", "startTime": "2023-01-13T18:04:10Z" } ], "metrics": { "performedShipmentCount": 2, "travelDuration": "1120s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "800s", "totalDuration": "1920s", "travelDistanceMeters": 3995 }, "routeCosts": { "model.vehicles.cost_per_kilometer": 39.95, "model.vehicles.cost_per_hour": 21.333333333333332 }, "routeTotalCost": 61.283333333333331 } ], "skippedShipments": [ { "index": 1 } ], "metrics": { "aggregatedRouteMetrics": { "performedShipmentCount": 2, "travelDuration": "1120s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "800s", "totalDuration": "1920s", "travelDistanceMeters": 3995 }, "usedVehicleCount": 1, "earliestVehicleStartTime": "2023-01-13T17:37:49Z", "latestVehicleEndTime": "2023-01-13T18:09:49Z", "totalCost": 81.283333333333331, "costs": { "model.shipments.penalty_cost": 20, "model.vehicles.cost_per_hour": 21.333333333333332, "model.vehicles.cost_per_kilometer": 39.95 } } }
בדוגמה הזו, חלון הזמן המאוחר יותר גרם לדילוג על shipment[1]
,
כי זמן ההפעלה הנוסף של הרכב שנדרש כדי להשלים את
המסירה במסגרת חלון הזמן שצוין חרגה מעלויות המשלוח עלינו.
עלות הקנס על shipment[1]
מופיעה בmetrics.costs
, והמדד שלו
מופיעה ב-skippedShipments
.
מגבלות לחלון זמן רך
כפי שצוין בקצרה בקטע פרמטרים של מודל עלות, אפשר להחיל חלונות זמן כמגבלות רכות. מגבלות רכות שונות ממגבלות קשות באופן הבא:
- מגבלות חמורות: לא ניתן להפר אותן, וכלי האופטימיזציה לא מציע שמפר את האילוץ, גם אם המשמעות היא לדלג על משלוח.
- מגבלות רכות: ייתכן שיש הפרה של המדיניות, כלומר כלי האופטימיזציה מספקת פתרון שמפר מגבלה רכה. אבל כלי האופטימיזציה מחילה גם עלות על כל הפרה. אתם מציינים את העלות הזו עוד נכס בחלון הזמן, בדרך כלל כעלות לשעה עבור בכל שעה לפני או אחרי חלון הזמן שבו הפעילות מתרחשת.
חלונות הזמן מרוככים באמצעות softStartTime
או softEndTime
במקום
startTime
או endTime
בהתאמה, ולפי הגדרה
costPerHourBeforeSoftStartTime
או costPerHourAfterSoftEndTime
.
להפעיל מגבלות בחלון זמן רך כשאמורים לבצע איסוףים או משלוחים במסגרת חלון זמן מוגדר, אבל איסוף או משלוח במסגרת החלון הזה לא הוא הכרחי. אפשר להשתמש באילוצים של חלונות זמן רכים וקשים יחד כדי לשקף יעדים עסקיים. לדוגמה:
- חלון זמן קשה: מציין את שעות הפעילות של הלקוח, למשל מתאריך 9:00 עד 17:00.
- חלון זמן רך: מציין את מסגרת הזמן למשלוח או לאיסוף תואם להודעה שנשלחה ללקוח, למשל מ-9:00 עד 13:00.
בדוגמה הזו, המשלוח שדילג עליו בעבר כי הזמן שלו החלון התחיל מאוחר מדי ואילוץ זמן ההתחלה מרוכך. השני היה חלון זמן למשלוחים גם זמני הסיום מתרככים.
הצגת בקשה לדוגמה עם מרווח זמן ארוך וקשה חלונות
{ "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", "timeWindows": [ { "startTime": "2023-01-13T18:00:00Z", "softEndTime": "2023-01-13T19:00:00Z", "costPerHourAfterSoftEndTime": 2.0 } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 100.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.789116, "longitude": -122.395080 }, "duration": "250s", "timeWindows": [ { "softStartTime": "2023-01-13T21:00:00Z", "endTime": "2023-01-13T21:30:00Z", "costPerHourBeforeSoftStartTime": 2.0 } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 20.0 }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.795242, "longitude": -122.399347 }, "duration": "250s", "timeWindows": [ { "startTime": "2023-01-13T17:30:00Z", "softEndTime": "2023-01-13T18:00:00Z", "costPerHourAfterSoftEndTime": 2.0 } ] } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 50.0 } ], "vehicles": [ { "endLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "startLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "costPerHour": 40.0, "costPerKilometer": 10.0 } ] } }
לראות תשובה לבקשה לדוגמה עם טקסט חלונות זמן רך
{ "routes": [ { "vehicleStartTime": "2023-01-13T17:48:35Z", "vehicleEndTime": "2023-01-13T18:24:28Z", "visits": [ { "isPickup": true, "startTime": "2023-01-13T17:48:35Z", "detour": "0s" }, { "shipmentIndex": 1, "isPickup": true, "startTime": "2023-01-13T17:51:05Z", "detour": "150s" }, { "shipmentIndex": 2, "isPickup": true, "startTime": "2023-01-13T17:53:35Z", "detour": "300s" }, { "startTime": "2023-01-13T18:00:00Z", "detour": "300s" }, { "shipmentIndex": 1, "startTime": "2023-01-13T18:07:42Z", "detour": "493s" }, { "shipmentIndex": 2, "startTime": "2023-01-13T18:17:27Z", "detour": "873s" } ], "transitions": [ { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:48:35Z" }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:51:05Z" }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T17:53:35Z" }, { "travelDuration": "235s", "travelDistanceMeters": 795, "waitDuration": "0s", "totalDuration": "235s", "startTime": "2023-01-13T17:56:05Z" }, { "travelDuration": "212s", "travelDistanceMeters": 791, "waitDuration": "0s", "totalDuration": "212s", "startTime": "2023-01-13T18:04:10Z" }, { "travelDuration": "335s", "travelDistanceMeters": 1204, "waitDuration": "0s", "totalDuration": "335s", "startTime": "2023-01-13T18:11:52Z" }, { "travelDuration": "171s", "travelDistanceMeters": 665, "waitDuration": "0s", "totalDuration": "171s", "startTime": "2023-01-13T18:21:37Z" } ], "metrics": { "performedShipmentCount": 3, "travelDuration": "953s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2153s", "travelDistanceMeters": 3455 }, "routeCosts": { "model.shipments.deliveries.time_windows.cost_per_hour_after_soft_end_time": 0.58166666666666667, "model.shipments.deliveries.time_windows.cost_per_hour_before_soft_start_time": 5.7433333333333332, "model.vehicles.cost_per_hour": 23.922222222222221, "model.vehicles.cost_per_kilometer": 34.55 }, "routeTotalCost": 64.797222222222217 } ], "metrics": { "aggregatedRouteMetrics": { "performedShipmentCount": 3, "travelDuration": "953s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2153s", "travelDistanceMeters": 3455 }, "usedVehicleCount": 1, "earliestVehicleStartTime": "2023-01-13T17:48:35Z", "latestVehicleEndTime": "2023-01-13T18:24:28Z", "totalCost": 64.797222222222217, "costs": { "model.vehicles.cost_per_kilometer": 34.55, "model.shipments.deliveries.time_windows.cost_per_hour_before_soft_start_time": 5.7433333333333332, "model.shipments.deliveries.time_windows.cost_per_hour_after_soft_end_time": 0.58166666666666667, "model.vehicles.cost_per_hour": 23.922222222222221 } } }
כאשר דילגת לגמרי על הדוגמה עם מגבלות של חלון זמן קשים בלבד
shipment[1]
, אם ריכוך את חלון זמן האספקה שלו, המוצר יישלח אליך
לפני שעת ההתחלה של חלון הזמן. באותו אופן, ריכוך זמני הסיום
משלוחים אחרים אפשרו מסירה של shipment[2]
אחרי חלון הזמן שהוגדר
מסתיים.
במקביל, השתנו העלויות וסך כל המשלוחים:
totalCost
: ירידה מ-81.283 ל-64.797- סה"כ משלוחים שהושלמו: גדל מ-2 ל-3
כלי האופטימיזציה מצא פתרון זול יותר, מפני שחלון הזמן המגבלות היו רגועות יותר בהשוואה לדוגמה הקודמת.
לבסוף, המאפיין metrics.costs
כולל גם מפתח חדש שמציין את
העלות בפועל שנצברו בהתאם למכפלה של האילוץ ואורך
הזמן שחלון המשלוח לא חלף. כלומר:
costPerHourBeforeSoftStartTime
מתוך 2.0 ו-- משך הזמן בין המסירה בפועל לבין תחילת חלון הזמן: 2.83583 שעות
תוצאה:
model.shipments.deliveries.time_windows.cost_per_hour_before_soft_start_time
:
5.6716666666666669.
המדדים האלה מאפשרים לבצע ניתוח עלויות כדי למצוא את האיזון בין אתגרים
מגבלות ומגבלות רכות, שאפשר להשתמש בהן כדי לכוונן את האילוצים
שיתאימו יותר לכללים העסקיים הספציפיים שלכם. במקרה הזה, העלות הכוללת היא
פחות מ-shipment[1].penalty_cost
מתוך 20.0. כלי האופטימיזציה זיהה
שהוא חסכוני יותר לספק את המשלוח מוקדם יותר ממה שהוא אמור
מדלגים על המשלוח.