Żądania i limity obciążenia

W tym przewodniku opisujemy funkcje loadDemands i loadLimits oraz ich wzajemne powiązania.

Jak wspomniano w sekcji Ograniczenia dotyczące czasu odbioru i dostawy, wiadomość OptimizeToursRequest (REST, gRPC) zawiera wiele właściwości, które określają ograniczenia związane z problemem, który ma być optymalizowany. Kilka właściwości OptimizeToursRequest reprezentuje ograniczenia obciążenia.

Pojazdy i przesyłki mają właściwości fizyczne, które należy wziąć pod uwagę podczas planowania trasy.

  • Pojazdy: właściwość loadLimits określa maksymalne obciążenie, jakie może wytrzymać pojazd. Zapoznaj się z dokumentacją dotyczącą wiadomości Vehicle (REST, gRPC).
  • Przesyłki: właściwość loadDemands określa, ile zasobów wykorzystuje dana przesyłka. Zapoznaj się z dokumentacją dotyczącą wiadomości Shipment (REST, gRPC).

Te 2 ograniczenia umożliwiają optymalizatorowi odpowiednie przypisywanie przesyłek do pojazdów w sposób, który najlepiej pasuje do pojemności Twojej floty i wymagań dotyczących przesyłek.

W pozostałej części tego dokumentu szczegółowo omawiamy loadLimitsloadDemands.

Zapotrzebowania i limity obciążenia: typy

Każde obciążenie i ograniczenie wyraża się za pomocą typu.

Możesz podać własny zestaw typów wczytywania, np.:

  • waga
  • głośność
  • pomiary liniowe
  • nazwy przewożonych przedmiotów lub sprzętu;

W tym przewodniku jako przykładowy typ danych użyto typu weightKg.

Zarówno Shipment.loadDemands, jak i Vehicle.loadLimits używają typu Protocol Buffers (map) z kluczami string, które reprezentują typy ładowania.

Wartości Shipment.loadDemands używają komunikatu Load (REST, gRPC). Wiadomość Load ma jedną właściwość amount, która określa, ile miejsca jest wymagane do zrealizowania wysyłki określonego typu.

Wartości Vehicle.loadLimits używają komunikatu LoadLimit (REST, gRPC). Wiadomość LoadLimit ma kilka właściwości, a wartość maxLoad reprezentuje maksymalną ładowność pojazdu dla określonego typu.

loadDemands przesyłki zużywa loadLimits przypisanego pojazdu tylko wtedy, gdy oba mają pasujące klucze typu ładunku. Na przykład przesyłka z: loadDemands

"loadDemands": {
  "weightKg": {
    "amount": 50
  }
}

wymaga 50 jednostek ładunku typu weightKg, aby przesyłka mogła zostać zrealizowana. Pojazd z loadLimits:

"loadLimits": {
  "weightKg": {
    "maxLoad": 100
  }
}

może zrealizować dostawę, ponieważ maxLoad pojazdu typu weightKg jest większa lub równa loadDemands przesyłki typu weightKg. Jednak pojazd z loadLimits:

"loadLimits": {
  "equipmentRackStorage": {
    "maxLoad": 10
  }
}

ma nieograniczoną pojemność weightKg ze względu na brak limitu ładunku weightKg, więc pojazd nie jest ograniczony przez wymaganą wagę przesyłki.

Przenoszenie ładunku między przesyłkami i pojazdami

Podczas odbioru i dostawy przesyłek przez pojazdy loadDemand przesyłane są między przesyłką a pojazdem. Załadowania pojazdu możesz zobaczyć w wiadomości OptimizeToursResponse (REST, gRPC)routes.transitions dla danego pojazdu. Sekwencja jest następująca:

  1. Wymagana pojemność ładunku jest definiowana dla przesyłki jako loadDemand.
  2. Przesyłka jest odbierana przez przypisany do niej pojazd, a jego vehicleLoads wzrasta o wartość loadDemand przesyłki. Ten transfer jest reprezentowany przez positive visits.loadDemands w odpowiedzi na wiadomość.
  3. Pojazd dostarcza przesyłkę, a jego vehicleLoads zmniejsza się o wartość loadDemand dostarczonej przesyłki. To przeniesienie jest reprezentowane przez ujemną wartość visits.loadDemands w wiadomości z odpowiedzią.

vehicleLoads pojazdu nie może w żadnym momencie trasy przekraczać określonej wartości loadLimits.

Pełny przykład z wymaganiami i ograniczeniami dotyczącymi obciążenia

Przykład żądania z wymaganiami dotyczącymi obciążenia i limitami

{
  "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
          }
        }
      }
    ]
  }
}
    

Przykładowe żądanie zawiera kilka parametrów związanych z obciążeniem:

  • shipments[0] ma obciążenie 50 weightKg.
  • shipments[1] ma zapotrzebowanie na moc 10 weightKg.
  • shipments[2] ma obciążenie 80 weightKg.
  • vehicles[0] ma limit wczytywania wynoszący 100 weightKg.

wyświetlić odpowiedź na żądanie z wymaganiami dotyczącymi obciążenia i limitami;

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

Dodane ograniczenia obciążenia wpływają na kolejność visits:

  1. shipment[0] zostało odebrane
  2. shipment[1] zostało odebrane
  3. Zrealizowano usługę shipment[0]
  4. Zrealizowano usługę shipment[1]
  5. shipment[2] zostało odebrane
  6. Zrealizowano usługę shipment[2]

W tym zamówieniu 3 przesyłki nie mogą być zrealizowane jednocześnie, ponieważ ich łączna waga loadDemands przekracza loadLimits pojazdu.

Każdy wpis visits zawiera zmianę ładunku pojazdu wynikającą z ukończenia Visit. Wartości dodatnie oznaczają załadowanie przesyłki, a ujemne – jej rozładunek.

Każdy wpis transitions zawiera łączne obciążenie pojazdu w trakcie Transition. Na przykład strona transitions[2] ma 60 poziomów obciążenia weightKg, co odpowiada łącznym obciążeniom shipment[0]shipment[1].

Obiekty danych routes[0].metricsmetrics.aggregatedRouteMetrics zawierają właściwość maxLoads. Wartość typu weightKg to 80, co oznacza część trasy pojazdu, która transportowała shipments[2] do miejsca dostawy.

Ograniczenia dotyczące limitu wczytywania

Podobnie jak w przypadku okien czasowych opisanych w sekcji Ograniczenia dotyczące okien odbioru i dostawy, limity obciążenia mają warianty twarde i miękkie. Właściwość maxLoad wiadomości LoadLimit wyraża twarde ograniczenie: pojazd nigdy nie może przewozić ładunku przekraczającego wartość maxLoad w określonym typie. Właściwości softMaxLoadcostPerUnitAboveSoftMax określają ograniczenie łagodne, przy czym każda jednostka przekraczająca wartość softMaxLoad powoduje koszt costPerUnitAboveSoftMax.

Ograniczenia o miękkim limicie obciążenia mają kilka zastosowań, takich jak:

  • równoważenie przesyłek na większej liczbie pojazdów niż minimalna wymagana liczba, gdy jest to opłacalne;
  • wyrażenie preferencji kierowcy dotyczących liczby elementów, które może wygodnie odebrać i dostarczyć na danej trasie;
  • ładowanie pojazdów poniżej ich maksymalnej fizycznej pojemności, aby ograniczyć zużycie i obniżyć koszty konserwacji;

Ograniczenia twardego i miękkiego limitu obciążenia można stosować jednocześnie. Na przykład sztywny limit ładunku może określać maksymalną wagę ładunku, który pojazd może bezpiecznie przewieźć, lub maksymalną liczbę elementów, które mieszczą się w pojeździe w danym momencie. Z kolei miękki limit ładunku może określać maksymalną wagę lub liczbę elementów, które stwarzają problemy kierowcy w przypadku spakowania wszystkiego w pojeździe.