עומסים ומגבלות

מפתחים באזור הכלכלי האירופי (EEA)

במדריך הזה נסביר על 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 של רכב מסוים. הרצף הוא כדלקמן:

  1. קיבולת העומס הנדרשת מוגדרת למשלוח כ-loadDemand.
  2. המשלוח נאסף על ידי הרכב שהוקצה לו, והערך של vehicleLoads של הרכב גדל בכמות של loadDemand של המשלוח. ההעברה הזו מיוצגת על ידי positive visits.loadDemands בהודעת התגובה.
  3. הרכב מעביר את המשלוח, וכמות 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] הוא 50 weightKg.
  • ל-shipments[1] יש דרישת עומס של 10 weightKg.
  • ל-shipments[2] יש ביקוש לטעינה של 80 weightKg.
  • vehicles[0] יש מגבלת טעינה של 100 weightKg.

לראות תגובה לבקשה עם דרישות וגבולות טעינה

{
  "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:

  1. ההזמנה מshipment[0] נאספה
  2. ההזמנה מshipment[1] נאספה
  3. הפריט shipment[0] נמסר
  4. הפריט shipment[1] נמסר
  5. ההזמנה מshipment[2] נאספה
  6. הפריט 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.

למגבלות על עומס רך יש כמה שימושים, למשל:

  • חלוקת המשלוחים בין יותר כלי רכב מהמספר המינימלי הנדרש, אם זה משתלם מבחינת עלות
  • העדפת הנהג לגבי מספר הפריטים שהוא יכול לאסוף ולמסור בנוחות במסלול נתון
  • טעינת כלי רכב מתחת לקיבולת הפיזית המקסימלית שלהם כדי להגביל את הבלאי ולהפחית את עלויות התחזוקה

אפשר להשתמש ביחד באילוצים של מגבלת טעינה קשיחה ומגבלת טעינה רכה. לדוגמה, מגבלת עומס קשיחה יכולה להיות המשקל המקסימלי של המטען שכלי רכב יכול לשאת בבטחה או מספר הפריטים המקסימלי שיכולים להיכנס לכלי רכב בכל פעם, בעוד שמגבלת עומס רכה יכולה להיות המשקל המקסימלי או מספר הפריטים שיקשו על הנהג להכניס את הכול לכלי הרכב.