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

במדריך הזה נתאר את loadDemands ואת loadLimits, ונציג את הקשר ביניהם.

כפי שצוין במגבלות של חלונות זמן איסוף ואספקה, הודעת OptimizeToursRequest (REST, gRPC) מכילה מספר מאפיינים שמציינים מגבלות לבעיה באופטימיזציה. כמה המאפיינים של OptimizeToursRequest מייצגים מגבלות טעינה.

לכלי רכב ולמשלוחים יש מאפיינים פיזיים שצריך להביא בחשבון כשמתכננים מסלול.

  • כלי רכב: המאפיין loadLimits מציין את העומס המקסימלי שכלי הרכב יכול לשאת. אפשר לעיין במסמכי העזרה של ההודעה Vehicle (REST,‏ gRPC).
  • משלוחים: המאפיין loadDemands מציין את כמות הטעינה הנתונה הצריכה. אפשר לעיין במסמכי העזרה של ההודעה Shipment (REST,‏ gRPC).

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

שאר המסמך הזה עוסק בהרחבה בloadLimits ובloadDemands.

עומסי ביקוש ומגבלות: סוגים

אתם מציינים כל אילוץ של ביקוש והגבלה מבחינת סוג.

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

  • משקל
  • עוצמת קול
  • מדידות ליניאריות
  • שמות הפריטים או הציוד שנשלחים

במדריך הזה נעשה שימוש ב-weightKg כסוג לדוגמה.

גם Shipment.loadDemands וגם Vehicle.loadLimits משתמשים בסוג map של Protocol Buffers, עם מפתחות 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 של המשלוח. ההעברה הזו מיוצגת על ידי 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 של 60, שמייצג את העומסים המשולבים של shipment[0] ו-shipment[1].

האובייקטים של המדדים routes[0].metrics ו-metrics.aggregatedRouteMetrics כוללים נכס maxLoads. הערך של הסוג weightKg הוא 80, והוא מייצג את החלק במסלול של הרכב שבו הועבר shipments[2] למיקום המסירה.

מגבלות על עומס רך

בדומה לחלונות הזמן שמפורטים בקטע אילוצים של חלונות זמן לאיסוף ולמסירה, לאילוצים של מגבלת עומס יש וריאנט קשיח ווריאנט רך. המאפיין maxLoad של ההודעה LoadLimit מייצג אילוץ קשיח: אסור שהרכב יישא עומס שחורג מהערך של maxLoad בסוג שצוין. המאפיינים softMaxLoad ו-costPerUnitAboveSoftMax מייצגים אילוץ רך, וכל יחידה שמחריגה מ-softMaxLoad יוצרת עלות של costPerUnitAboveSoftMax.

למגבלות של מגבלת טעינה רכה יש כמה שימושים, כמו למשל:

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

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