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

מפתחים באזור הכלכלי האירופי (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.

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

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

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