Żądania i limity obciążenia

W tym przewodniku opisujemy atrybuty loadDemands i loadLimits oraz ich związek ze sobą.

Jak wspomnieliśmy w sekcji Ograniczenia przedziału czasu odbioru i dostawy, wiadomość OptimizeToursRequest (REST, gRPC) zawiera szereg właściwości, które określają ograniczenia optymalizowanego problemu. Kilka właściwości OptimizeToursRequest określa 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, które może obsłużyć pojazd. Zapoznaj się z dokumentacją wiadomości Vehicle (REST, gRPC).
  • Przesyłki: właściwość loadDemands określa ilość obciążenia pochłanianego przez daną przesyłkę. Zapoznaj się z dokumentacją wiadomości Shipment (REST, gRPC).

Dzięki połączeniu tych 2 ograniczeń optymalizator może odpowiednio przypisywać przesyłki do pojazdów w sposób najlepiej odpowiadający pojemności floty i zapotrzebowaniu w zakresie dostaw.

W pozostałej części tego dokumentu szczegółowo omówione są loadLimits i loadDemands.

Obciążenia i limity: typy

Każde zapotrzebowanie na obciążenie i ograniczenie ograniczenia podajesz w postaci typu.

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

  • waga
  • wolumin
  • pomiary liniowe
  • nazwy przewożonych przedmiotów lub sprzętu;

W tym przewodniku jako przykładowego typu użyto pola weightKg.

Shipment.loadDemands i Vehicle.loadLimits używają typu Bufory protokołu map, gdzie klucze string reprezentują typy obciążenia.

Wartości Shipment.loadDemands używają komunikatu Load (REST, gRPC). Wiadomość Load ma 1 właściwość amount, która określa, jaka pojemność jest wymagana do złożenia zamówienia w określonym typie.

Wartości Vehicle.loadLimits używają komunikatu LoadLimit (REST, gRPC). Komunikat LoadLimit ma kilka właściwości, z których maxLoad oznacza maksymalną pojemność pojazdu w danym typie.

loadDemands przesyłki wykorzystuje dane loadLimits przypisanego pojazdu tylko wtedy, gdy oba te elementy mają pasujące klucze typu obciążenia. Na przykład przesyłka z loadDemands o wartości:

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

wymaga 50 jednostek wczytywania typu weightKg, aby można było zrealizować dostawę. Pojazd, w którym masz loadLimits:

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

może zrealizować dostawę, ponieważ wartość maxLoad pojazdu weightKg jest większa lub równa loadDemands dostawy w typie weightKg. Jednak pojazd, którego loadLimits ma:

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

domyślnie ma nieograniczoną pojemność wynoszącą weightKg ze względu na brak limitu obciążenia wynoszącego weightKg, więc pojazd nie jest ograniczony przez wagę przesyłki.

Przewóz ładunku między przesyłkami a pojazdami

Gdy przesyłki są odbierane i dostarczane przez pojazdy, loadDemand przesyłki jest przenoszony między nią a pojazdem. Obciążenie pojazdu możesz zobaczyć we wpisie OptimizeToursResponse (REST, gRPC)routes.transitions dla danego pojazdu. Sekwencja wygląda tak:

  1. Wymagana nośność obciążenia jest określona dla przesyłki jako loadDemand.
  2. Przesyłka zostanie odebrana przez przypisany do niego pojazd, a vehicleLoads pojazdu zwiększy się o loadDemand. Przeniesienie jest oznaczone przez dodatnią wartość visits.loadDemands w wiadomości z odpowiedzią.
  3. Pojazd dostarczy przesyłkę, a kwota vehicleLoads pojazdu zmniejsza się o wartość loadDemand dostarczonej przesyłki. To przeniesienie jest reprezentowane przez ujemne wartości visits.loadDemands w wiadomości z odpowiedzią.

Wartość vehicleLoads pojazdu nie może przekroczyć określonej wartości loadLimits w żadnym punkcie trasy.

Pełny przykład obciążenia i limitów obciążenia

Zobacz przykładowe żądanie z wymaganiami i limitami obciążenia

{
  "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 na poziomie 50 weightKg.
  • shipments[1] ma obciążenie na poziomie 10 weightKg.
  • shipments[2] ma obciążenie na poziomie 80 weightKg.
  • vehicles[0] ma limit wczytywania wynoszący 100 weightKg.

Wyświetl odpowiedź na żądanie z żądaniami 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ść elementów visits:

  1. Odebrano: shipment[0]
  2. Odebrano: shipment[1]
  3. Dostarczono: shipment[0]
  4. Dostarczono: shipment[1]
  5. Odebrano: shipment[2]
  6. Dostarczono: shipment[2]

Z tego zamówienia wynika, że pojazd nie może zrealizować 3 przesyłek jednocześnie, ponieważ łączna loadDemands przekracza loadLimits.

Każdy wpis visits obejmuje zmianę w obciążeniu pojazdu wynikającą z uzupełnienia danych Visit. Wartości dodatnie reprezentują wczytanie przesyłki, a ujemne – wyłapywanie przesyłki.

Każda pozycja transitions uwzględnia łączne obciążenie pojazdu w okresie Transition. Na przykład transitions[2] ma obciążenie weightKg równe 60, co odpowiada łącznemu wczytywaniu stron shipment[0] i shipment[1].

Obiekty wskaźników routes[0].metrics i metrics.aggregatedRouteMetrics zawierają właściwość maxLoads. Wartość dla typu weightKg wynosi 80, co oznacza część trasy pojazdu, która przetransportowała obiekt shipments[2] do miejsca dostawy.

Ograniczenia nieblokującego wczytywania

Tak jak w przypadku przedziałów czasu opisanych w sekcji Ograniczenia dotyczące przedziału czasu odbioru i dostawy, ograniczenia dotyczące limitów obciążenia mają wersje sztywne i łagodne. Właściwość maxLoad w komunikacie LoadLimit określa sztywne ograniczenie: pojazd nie może nigdy przenosić ładunku przekraczającego wartość maxLoad w podanym typie. Właściwości softMaxLoad i costPerUnitAboveSoftMax wskazują na łagodne ograniczenie, przy czym każda jednostka przekracza softMaxLoad, a cena wynosi costPerUnitAboveSoftMax.

Ograniczenia nieograniczonego obciążenia mają kilka zastosowań:

  • równoważenie wysyłki większej liczby pojazdów niż niezbędna do uzyskania minimalnej liczby, jeśli jest to opłacalne
  • Przez wskazanie kierowcy, który jako kierowcy może wygodnie odebrać i przewieźć dany przedmiot
  • ładowania pojazdów do maksymalnej pojemności fizycznej, aby zmniejszyć zużycie i koszty konserwacji

Ograniczeń twardego i niezmiennego obciążenia można używać jednocześnie. Na przykład limit twardego może wskazywać maksymalną ciężar ładunku, który może bezpiecznie przewieźć pojazd, lub maksymalną liczbę przedmiotów, które zmieści się w pojeździe jednorazowo. Limit ładunku to maksymalna waga lub liczba przedmiotów, które zmieściłyby się w pojeździe.