توجيه الأساطيل: تخصيص الشحنات للمركبات

يوضّح هذا الدليل كيف يمكن أن يختلف عدد المركبات المقدَّمة في حلّ "تحسين المسارات" استنادًا إلى مَعلمات الطلب.

لا تعمل واجهة برمجة التطبيقات Route Optimization API على تحسين ترتيب إكمال الشحنات فحسب، بل تشكل هذه الواجهة أيضًا هذه الشحنات إلى المركبات من أجل تحسين التكاليف ضمن القيود التي تديرها.

في المثال الأول، يتطابق عدد المركبات مع عدد الشحنات، مع مشاركة جميع المركبات لسمات التكلفة والموقع الجغرافي نفسها. لكل مركبة تكلفة لكل ساعة تشغيل وتكلفة لكل كيلومتر مقطوع، ما يساعد في تقليل وقت السفر والمسافة. قد يتوقع المرء أن يتم تحديد شحنات لعدة مركبات، ولكن يعرض مثال الردّ الحلّ الأقل تكلفة استنادًا إلى مَعلمات نموذج التكلفة المحدّدة.

الاطّلاع على مثال لطلب يتضمّن مركبات متعددة

{
  "model": {
    "globalStartTime": "2023-01-13T16:00:00-08:00",
    "globalEndTime": "2023-01-14T16:00:00-08:00",
    "shipments": [
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 37.789456,
              "longitude": -122.390192
            },
            "duration": "250s"
          }
        ],
        "pickups": [
          {
            "arrivalLocation": {
              "latitude": 37.794465,
              "longitude": -122.394839
            },
            "duration": "150s"
          }
        ],
        "penaltyCost": 100.0
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 37.789116,
              "longitude": -122.395080
            },
            "duration": "250s"
          }
        ],
        "pickups": [
          {
            "arrivalLocation": {
              "latitude": 37.794465,
              "longitude": -122.394839
            },
            "duration": "150s"
          }
        ],
        "penaltyCost": 5.0
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 37.795242,
              "longitude": -122.399347
            },
            "duration": "250s"
          }
        ],
        "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": 50.0,
        "costPerKilometer": 10.0
      },
      {
        "endLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "startLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "costPerHour": 50.0,
        "costPerKilometer": 10.0
      },
      {
        "endLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "startLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "costPerHour": 50.0,
        "costPerKilometer": 10.0
      }
    ]
  }
}
    

الاطّلاع على ردّ على الطلب يتضمّن عدة سيارات

{
  "routes": [
    {
      "vehicleStartTime": "2023-01-14T00:00:00Z",
      "vehicleEndTime": "2023-01-14T00:28:22Z",
      "visits": [
        {
          "isPickup": true,
          "startTime": "2023-01-14T00:00:00Z",
          "detour": "0s"
        },
        {
          "shipmentIndex": 2,
          "isPickup": true,
          "startTime": "2023-01-14T00:02:30Z",
          "detour": "150s"
        },
        {
          "startTime": "2023-01-14T00:08:55Z",
          "detour": "150s"
        },
        {
          "shipmentIndex": 2,
          "startTime": "2023-01-14T00:21:21Z",
          "detour": "572s"
        }
      ],
      "transitions": [
        {
          "travelDuration": "0s",
          "waitDuration": "0s",
          "totalDuration": "0s",
          "startTime": "2023-01-14T00:00:00Z"
        },
        {
          "travelDuration": "0s",
          "waitDuration": "0s",
          "totalDuration": "0s",
          "startTime": "2023-01-14T00:02:30Z"
        },
        {
          "travelDuration": "235s",
          "travelDistanceMeters": 795,
          "waitDuration": "0s",
          "totalDuration": "235s",
          "startTime": "2023-01-14T00:05:00Z"
        },
        {
          "travelDuration": "496s",
          "travelDistanceMeters": 1893,
          "waitDuration": "0s",
          "totalDuration": "496s",
          "startTime": "2023-01-14T00:13:05Z"
        },
        {
          "travelDuration": "171s",
          "travelDistanceMeters": 665,
          "waitDuration": "0s",
          "totalDuration": "171s",
          "startTime": "2023-01-14T00:25:31Z"
        }
      ],
      "metrics": {
        "performedShipmentCount": 2,
        "travelDuration": "902s",
        "waitDuration": "0s",
        "delayDuration": "0s",
        "breakDuration": "0s",
        "visitDuration": "800s",
        "totalDuration": "1702s",
        "travelDistanceMeters": 3353
      },
      "routeCosts": {
        "model.vehicles.cost_per_kilometer": 33.53,
        "model.vehicles.cost_per_hour": 23.638888888888889
      },
      "routeTotalCost": 57.168888888888887
    },
    {
      "vehicleIndex": 1
    },
    {
      "vehicleIndex": 2
    }
  ],
  "skippedShipments": [
    {
      "index": 1
    }
  ],
  "metrics": {
    "aggregatedRouteMetrics": {
      "performedShipmentCount": 2,
      "travelDuration": "902s",
      "waitDuration": "0s",
      "delayDuration": "0s",
      "breakDuration": "0s",
      "visitDuration": "800s",
      "totalDuration": "1702s",
      "travelDistanceMeters": 3353
    },
    "usedVehicleCount": 1,
    "earliestVehicleStartTime": "2023-01-14T00:00:00Z",
    "latestVehicleEndTime": "2023-01-14T00:28:22Z",
    "totalCost": 62.168888888888887,
    "costs": {
      "model.vehicles.cost_per_hour": 23.638888888888889,
      "model.shipments.penalty_cost": 5,
      "model.vehicles.cost_per_kilometer": 33.53
    }
  }
}
    

يحدِّد الحلّ جميع الشحنات لمركبة واحدة فقط، ويتخطّى شحنة واحدة على الرغم من توفّر مركبات كافية. ويعود ذلك إلى أنّ تكلفة تشغيل المركبات الإضافية مرتفعة جدًا ولا تُبرّر ذلك، كما أنّه ليس من المجدي من الناحية المالية استخدام أي مركبة لإكمال الشحنة التي تم تخطّيها نظرًا لانخفاض تكلفة العقوبة. على الرغم من سعة المركبة المتاحة، يمكن لمركبة واحدة تنفيذ جميع عمليات التحميل المخصّصة لها بأكثر الطرق فعالية من حيث التكلفة. لا يتم ضبط سمة usedIfRouteIsEmpty في المركبات المدرَجة في الطلب (اطّلِع على مستندات Vehicle الرسائل (REST وgRPC) للاطّلاع على التفاصيل)، لذا لا يتم تحمُّل أي تكلفة في حال عدم استخدامها.

يؤدي تغيير مَعلمات التكلفة لمنح الأولوية للحلول الأقصر على مستوى العالم بدلاً من مسارات المركبات الأقصر بشكل فردي إلى مشاركة المزيد من المركبات في الحلّ. يستبدل المثال التالي لطلب البحث Vehicle.costPerHour بShipmentModel.globalDurationCostPerHour الشاملة، ما يمنح الأولوية للحلول التي تستغرق وقتًا أقصر بشكل عام خلال وقت التشغيل لأي مركبة معيّنة. يتم أيضًا زيادة تكلفة الإجراء العقابي ضد shipment[1] لتقليل احتمال تخطّيه.

يمكنك الاطّلاع على مثال لطلب باستخدام globalDurationCostPerHour

{
  "model": {
    "globalStartTime": "2023-01-13T16:00:00-08:00",
    "globalEndTime": "2023-01-14T16:00:00-08:00",
    "globalDurationCostPerHour": 150.0,
    "shipments": [
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 37.789456,
              "longitude": -122.390192
            },
            "duration": "250s"
          }
        ],
        "pickups": [
          {
            "arrivalLocation": {
              "latitude": 37.794465,
              "longitude": -122.394839
            },
            "duration": "150s"
          }
        ],
        "penaltyCost": 100.0
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 37.789116,
              "longitude": -122.395080
            },
            "duration": "250s"
          }
        ],
        "pickups": [
          {
            "arrivalLocation": {
              "latitude": 37.794465,
              "longitude": -122.394839
            },
            "duration": "150s"
          }
        ],
        "penaltyCost": 75.0
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 37.795242,
              "longitude": -122.399347
            },
            "duration": "250s"
          }
        ],
        "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
        },
        "costPerKilometer": 10.0
      },
      {
        "endLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "startLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "costPerKilometer": 10.0
      },
      {
        "endLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "startLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "costPerKilometer": 10.0
      }
    ]
  }
}
    

توضِّح النتيجة أنّ استخدام مَعلمة التكلفة الإجمالية لكل ساعة يؤدي إلى استخدام كل المركبات الثلاث بدلاً من مركبة واحدة فقط.

يمكنك الاطّلاع على ردّ على الطلب باستخدام globalDurationCostPerHour

{
  "routes": [
    {
      "vehicleStartTime": "2023-01-14T00:00:00Z",
      "vehicleEndTime": "2023-01-14T00:16:20Z",
      "visits": [
        {
          "shipmentIndex": 2,
          "isPickup": true,
          "startTime": "2023-01-14T00:00:00Z",
          "detour": "0s"
        },
        {
          "shipmentIndex": 2,
          "startTime": "2023-01-14T00:09:19Z",
          "detour": "0s"
        }
      ],
      "transitions": [
        {
          "travelDuration": "0s",
          "waitDuration": "0s",
          "totalDuration": "0s",
          "startTime": "2023-01-14T00:00:00Z"
        },
        {
          "travelDuration": "409s",
          "travelDistanceMeters": 1371,
          "waitDuration": "0s",
          "totalDuration": "409s",
          "startTime": "2023-01-14T00:02:30Z"
        },
        {
          "travelDuration": "171s",
          "travelDistanceMeters": 665,
          "waitDuration": "0s",
          "totalDuration": "171s",
          "startTime": "2023-01-14T00:13:29Z"
        }
      ],
      "metrics": {
        "performedShipmentCount": 1,
        "travelDuration": "580s",
        "waitDuration": "0s",
        "delayDuration": "0s",
        "breakDuration": "0s",
        "visitDuration": "400s",
        "totalDuration": "980s",
        "travelDistanceMeters": 2036
      },
      "routeCosts": {
        "model.vehicles.cost_per_kilometer": 20.36
      },
      "routeTotalCost": 20.36
    },
    {
      "vehicleIndex": 1,
      "vehicleStartTime": "2023-01-14T00:00:00Z",
      "vehicleEndTime": "2023-01-14T00:18:54Z",
      "visits": [
        {
          "shipmentIndex": 1,
          "isPickup": true,
          "startTime": "2023-01-14T00:00:00Z",
          "detour": "0s"
        },
        {
          "shipmentIndex": 1,
          "startTime": "2023-01-14T00:08:24Z",
          "detour": "0s"
        }
      ],
      "transitions": [
        {
          "travelDuration": "0s",
          "waitDuration": "0s",
          "totalDuration": "0s",
          "startTime": "2023-01-14T00:00:00Z"
        },
        {
          "travelDuration": "354s",
          "travelDistanceMeters": 1192,
          "waitDuration": "0s",
          "totalDuration": "354s",
          "startTime": "2023-01-14T00:02:30Z"
        },
        {
          "travelDuration": "380s",
          "travelDistanceMeters": 1190,
          "waitDuration": "0s",
          "totalDuration": "380s",
          "startTime": "2023-01-14T00:12:34Z"
        }
      ],
      "metrics": {
        "performedShipmentCount": 1,
        "travelDuration": "734s",
        "waitDuration": "0s",
        "delayDuration": "0s",
        "breakDuration": "0s",
        "visitDuration": "400s",
        "totalDuration": "1134s",
        "travelDistanceMeters": 2382
      },
      "routeCosts": {
        "model.vehicles.cost_per_kilometer": 23.82
      },
      "routeTotalCost": 23.82
    },
    {
      "vehicleIndex": 2,
      "vehicleStartTime": "2023-01-14T00:00:00Z",
      "vehicleEndTime": "2023-01-14T00:16:14Z",
      "visits": [
        {
          "isPickup": true,
          "startTime": "2023-01-14T00:00:00Z",
          "detour": "0s"
        },
        {
          "startTime": "2023-01-14T00:06:25Z",
          "detour": "0s"
        }
      ],
      "transitions": [
        {
          "travelDuration": "0s",
          "waitDuration": "0s",
          "totalDuration": "0s",
          "startTime": "2023-01-14T00:00:00Z"
        },
        {
          "travelDuration": "235s",
          "travelDistanceMeters": 795,
          "waitDuration": "0s",
          "totalDuration": "235s",
          "startTime": "2023-01-14T00:02:30Z"
        },
        {
          "travelDuration": "339s",
          "travelDistanceMeters": 1276,
          "waitDuration": "0s",
          "totalDuration": "339s",
          "startTime": "2023-01-14T00:10:35Z"
        }
      ],
      "metrics": {
        "performedShipmentCount": 1,
        "travelDuration": "574s",
        "waitDuration": "0s",
        "delayDuration": "0s",
        "breakDuration": "0s",
        "visitDuration": "400s",
        "totalDuration": "974s",
        "travelDistanceMeters": 2071
      },
      "routeCosts": {
        "model.vehicles.cost_per_kilometer": 20.71
      },
      "routeTotalCost": 20.71
    }
  ],
  "metrics": {
    "aggregatedRouteMetrics": {
      "performedShipmentCount": 3,
      "travelDuration": "1888s",
      "waitDuration": "0s",
      "delayDuration": "0s",
      "breakDuration": "0s",
      "visitDuration": "1200s",
      "totalDuration": "3088s",
      "travelDistanceMeters": 6489
    },
    "usedVehicleCount": 3,
    "earliestVehicleStartTime": "2023-01-14T00:00:00Z",
    "latestVehicleEndTime": "2023-01-14T00:18:54Z",
    "totalCost": 112.14,
    "costs": {
      "model.vehicles.cost_per_kilometer": 64.89,
      "model.global_duration_cost_per_hour": 47.25
    }
  }
}
    

في هذا الردّ، تكون جميع المركبات الثلاث قيد الاستخدام (حسب metrics.usedVehicleCount) وتخصّص كل مركبة لشحنة واحدة لإكمالها. نظرًا لتطابق المواقع الجغرافية لنقطة البدء ونقطة النهاية وcostPerKilometer، يمكن تبديل المركبات الثلاث بفعالية، لذا لا يهمّ الشحنة التي يتم إسنادها إلى أي مركبة.

يؤدي الخيار globalDurationCostPerHour إلى أن يعثر المحسِّن على حلّ هو أقصر بشكل عام: لا يتجاوز الفرق بين earliestVehicleStartTime و latestVehicleEndTime 18 دقيقة و54 ثانية مقارنةً بـ 28 دقيقة و22 ثانية في الاستجابة السابقة. ومع ذلك، زاد القيمة المطلقة لـ metrics.costs.model.vehicles.cost_per_kilometer، ما يشير إلى زيادة في القيمة المطلقة للمسافة التي قطعتها المركبات الثلاث المستخدَمة. يوضّح ذلك إحدى الطرق التي يتيح لك فيها نموذج التكلفة إجراء مفاضلات:

  • زيادة التكلفة الزمنية الإجمالية: زيادة استخدام المركبات لتقليل إجمالي وقت التنفيذ، على حساب زيادة مسافة المركبات والوقت الذي يقضيه العميل في التنقّل
  • زيادة تكلفة الوقت المستغرَق في استخدام المركبة: يمكنك تقليل معدّل استخدام المركبة والوقت المستغرَق في التنقّل، ولكن سيؤدي ذلك إلى زيادة مدة الحلّ بشكل عام.

يُرجى العلم أنّ قيمة globalDurationCostPerHour التي تبلغ 150.0 في هذا المثال تم ضبطها بثلاثة أضعاف قيمة costPerHour للمركبات الفردية التي تبلغ 50.0 من المثال السابق. تتوقّع قيمة التكلفة الشاملة هذه بشكلٍ فعّال أن تعمل المركبات الثلاث بالتزامن، ولكن في الإعدادات العملية، قد لا تعكس هذه الافتراضات الواقع وقد يكون لها في الواقع تأثير سلبي على جودة النتائج.

كما هو موضّح في مَعلمات نموذج التكلفة، يتم التعبير عن جميع مَعلمات التكلفة باستخدام الوحدات نفسها التي لا تحتوي على أبعاد، ولكن يمكن أن يكون لها معانٍ مختلفة جدًا. عادةً، يجب أن تستند قيم مَعلمات نموذج التكلفة إلى الواقع قدر الإمكان، لأنّ التكاليف المصطنعة، مثل تلك الواردة في هذا المثال، قد تؤدي إلى تحسين واجهة برمجة التطبيقات لأجل أهداف لا تتطابق مع هدفك.