Żądania i limity obciążenia

W tym przewodniku opisano usługi loadDemands i loadLimits oraz ich wzajemny związek.

Jak wspomnieliśmy w sekcji Ograniczenia przedziału czasu odbioru i dostarczania, komunikat OptimizeToursRequest (REST, gRPC) zawiera kilka właściwości, które określają ograniczenia dotyczące optymalizowanego problemu. Kilka właściwości OptimizeToursRequest reprezentuje ograniczenia obciążenia.

Pojazdy i ładunki mają właściwości fizyczne, które należy wziąć pod uwagę przy planowaniu trasy.

  • Pojazdy: właściwość loadLimits określa maksymalne obciążenie, jakie może obsłużyć pojazd. Zapoznaj się z dokumentacją komunikatu Vehicle (REST, gRPC).
  • Przesyłki: właściwość loadDemands określa obciążenie zwiększoną przez daną przesyłkę. Zapoznaj się z dokumentacją komunikatu Shipment (REST, gRPC).

Te 2 ograniczenia umożliwiają optymalizatorowi odpowiednie przypisywanie dostaw do pojazdów w sposób najlepiej odpowiadający pojemności floty i zapotrzebowania na wysyłkę.

W pozostałej części tego dokumentu opisano szczegółowo loadLimits i loadDemands.

Żądania i limity związane z obciążeniem: typy

Wyrażasz każde żądanie obciążenia i ograniczenie limitu za pomocą typu.

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

  • waga
  • wolumin
  • pomiary liniowe
  • nazwy przenoszonych towarów lub sprzętu

W tym przewodniku przykładowy typ to weightKg.

Zarówno Shipment.loadDemands, jak i Vehicle.loadLimits używają typu Bufory protokołówmap, a klucze string reprezentują typy obciążenia.

Wartości Shipment.loadDemands używają komunikatu Load (REST, gRPC). Wiadomość Load zawiera 1 właściwość amount, która określa moc obliczeniową niezbędną do zrealizowania dostawy określonego typu.

Wartości Vehicle.loadLimits używają komunikatu LoadLimit (REST, gRPC). Komunikat LoadLimit ma kilka właściwości, przy czym maxLoad reprezentuje maksymalny udźwig pojazdu w określonym typie.

loadDemands przesyłki używa przypisanego do niej pojazdu loadLimits tylko wtedy, gdy oba mają pasujące klucze typu obciążenia. Na przykład dostawa z loadDemands tych produktów:

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

wymaga 50 jednostek obciążenia w typie weightKg, aby można było zrealizować dostawę. Pojazd z loadLimits z:

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

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

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

domyślnie ma nieograniczoną pojemność weightKg ze względu na brak limitu obciążenia (weightKg), więc pojazd nie jest ograniczony przez ciężar dostawy.

Przenoszenie ładunków między przesyłkami a pojazdami

W miarę dostarczania i odbierania przesyłek przez pojazdy transportuje się je loadDemand między przesyłką a pojazdem. Możesz sprawdzić ładunki pojazdu we wpisie OptimizeToursResponse (REST, gRPC)routes.transitions dla danego pojazdu. Sekwencja wygląda tak:

  1. Wymagana pojemność ładunkowa jest zdefiniowana dla przesyłki jako loadDemand.
  2. Przesyłka zostanie odebrana przez przypisany pojazd, a wartość vehicleLoads pojazdu wzrośnie o loadDemand. Ten transfer jest oznaczony w odpowiedzi jako pozytywny visits.loadDemands.
  3. Pojazd dostarcza przesyłkę, a vehicleLoads pojazdu zmniejsza się o kwotę loadDemand dostarczonej przesyłki. To przeniesienie jest oznaczone w odpowiedzi jako ujemne visits.loadDemands.

vehicleLoads pojazdu nie może na żadnym etapie trasy przekroczyć określonej wartości loadLimits.

Kompletny przykład wymagań 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 żądanie obciążenia równe 50 weightKg.
  • Zasób shipments[1] ma żądanie obciążenia równe 10 weightKg.
  • Zasób shipments[2] ma żądanie obciążenia równe 80 weightKg.
  • Limit obciążenia strony vehicles[0] wynosi 100 weightKg.

Zobacz odpowiedź na żądanie z wymaganiami i limitami obciążenia

{
  "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. 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 jednocześnie zrealizować 3 przesyłek, ponieważ łączna kwota loadDemands przekracza wartość loadLimits pojazdu.

Każda pozycja visits obejmuje zmianę obciążenia pojazdu konsekwencją uzupełnienia danych o Visit. Wartości dodatnie odzwierciedlają obciążenie przesyłki, a ujemne – wyładowywanie.

Każda pozycja transitions obejmuje łączne obciążenie pojazdu w okresie Transition. Na przykład element transitions[2] ma załadowane elementy typu weightKg o wartości 60, co oznacza łączne załadowania 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 i reprezentuje część trasy pojazdu, która skierowała shipments[2] do miejsca dostawy.

Ograniczenia limitu częściowego obciążenia

Tak jak w przypadku przedziałów czasu opisanych w artykule Ograniczenia przedziału czasu odbioru i dostawy, ograniczenia limitu obciążenia mają sztywne i miękkie warianty. Właściwość maxLoad komunikatu LoadLimit określa sztywne ograniczenie: pojazd nie może nigdy przenosić obciążenia przekraczającego wartość maxLoad w określonym typie. Właściwości softMaxLoad i costPerUnitAboveSoftMax zawierają łagodne ograniczenie, przy czym każda jednostka powyżej softMaxLoad wiąże się z kosztem costPerUnitAboveSoftMax.

Ograniczenia łagodnego obciążenia mają kilka zastosowań, na przykład:

  • zrównoważenie dostaw większej liczby pojazdów niż minimalna wymagana liczba pojazdów, gdy jest to opłacalne.
  • wyrażanie preferencji kierowców na temat liczby produktów, które można mu wygodnie odebrać i dostarczyć na danej trasie.
  • ładowanie pojazdów poniżej ich maksymalnej pojemności fizycznej, aby ograniczyć zużycie i koszty konserwacji

Twarde i łagodne ograniczenia obciążenia mogą być używane razem. Ograniczenie ładunku może na przykład określać maksymalną masę ładunku, jaką pojazd może bezpiecznie przewieźć, lub maksymalną liczbę towarów, które można w nim zmieścić w pojeździe jednocześnie, a miękki limit ładunku może określać maksymalną masę lub liczbę elementów, które ograniczałyby możliwość zmieszczenia kierowcy wszystkiego w pojeździe.