Żądania i limity obciążenia

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

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 problemu podlegającego optymalizacji. 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 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ą komunikatu Shipment (REST, gRPC).

Oba te 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ę w formie 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 danego 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ładunki 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 zostanie odebrana przez przypisany pojazd, a wartość vehicleLoads pojazdu wzrośnie o loadDemand. 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 oznaczone w odpowiedzi jako ujemne visits.loadDemands.

vehicleLoads pojazdu nie może na żadnym etapie trasy przekroczyć 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]

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ż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 limitu częściowego obciążenia

Podobnie 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 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;
  • określania kierowców liczby produktów, które mogą 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. 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 zmniejszają możliwości zmieszczenia kierowcy wszystkiego w pojeździe.