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:50shipments[1]
נמסר בשעה 18:00shipments[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. כלי האופטימיזציה זיהה
שהוא חסכוני יותר לספק את המשלוח מוקדם יותר ממה שהוא מציע
מדלגים על המשלוח.