Podstawowa optymalizacja obsługi zamówień z odbiorem i dostawą

Ten scenariusz optymalizuje kolejność przystanków przypisaną do pojazdu za pomocą prostych parametrów kosztów. To najprostszy tryb optymalizacji trasy, który zapewnia, że wszystkie przystanki zostaną odwiedzone we wskazanym okresie.

Poniższy przykład przedstawia podstawowy scenariusz z 1 pojazdem i 3 przesyłkami, które pochodzą z jednej lokalizacji nazywanej depot.

Zobacz przykładowe żądanie

      {
        "populatePolylines": true,
        "populateTransitionPolylines": true,
        "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"
                }
              ]
            },
            {
              "deliveries": [
                {
                  "arrivalLocation": {
                    "latitude": 37.789116,
                    "longitude": -122.395080
                  },
                  "duration": "250s"
                }
              ],
              "pickups": [
                {
                  "arrivalLocation": {
                    "latitude": 37.794465,
                    "longitude": -122.394839
                  },
                  "duration": "150s"
                }
              ]
            },
            {
              "deliveries": [
                {
                  "arrivalLocation": {
                    "latitude": 37.795242,
                    "longitude": -122.399347
                  },
                  "duration": "250s"
                }
              ],
              "pickups": [
                {
                  "arrivalLocation": {
                    "latitude": 37.794465,
                    "longitude": -122.394839
                  },
                  "duration": "150s"
                }
              ]
            }
          ],
          "vehicles": [
            {
              "endLocation": {
                "latitude": 37.794465,
                "longitude": -122.394839
              },
              "startLocation": {
                "latitude": 37.794465,
                "longitude": -122.394839
              },
              "costPerKilometer": 10.0,
              "costPerHour": 40.0
            }
          ]
        }
      }
    

Pola żądania optymalizacji trasy

Jak wspomnieliśmy w sekcji Omówienie, najważniejsze właściwości prośby o optymalizację trasy to vehicles i shipments.

Oprócz pojazdu i przesyłek żądanie zawiera te pola:

Linie łamane

populatePolylines i populateTransitionPolylines określają, czy optymalizacja tras ma zwracać linie łamane.

Usługa koduje Polylines za pomocą kodeka łamanego Map Google JS, który reprezentuje dane binarnych linii łamanych za pomocą możliwych do wydrukowania znaków ASCII. Aby zwizualizować ścieżki wygenerowane przez optymalizację tras, możesz użyć narzędzia interaktywnego kodera Polyline. W przykładzie w tym przewodniku ustawiamy wartości populatePolylines i populateTransitionPolylines na „true”, a inne ustawiają wartość „false” (fałsz), aby zmniejszyć rozmiar odpowiedzi.

Opis formatu kodowania znajdziesz w sekcji Format algorytmu kodowania łamanego kodu.

Globalne ograniczenia czasowe

Parametry model.globalStartTime i model.globalEndTime są ustawione na dowolny 24-godzinny okres. Dzięki temu wyjściowe sygnatury czasowe są łatwiejsze do interpretacji.

Odwiedź lokalizacje

Przykładowe żądanie używa tylko właściwości model.shipments[].pickups[].arrivalLocation i model.shipments[].deliveries[].arrivalLocation. Właściwość departureLocation występuje też w sytuacjach, gdy pojazd wyjeżdża z innego punktu niż tam, gdzie się pojawia. Przykładem może być kompleks parkingowy z wejściem po jednej stronie budynku i wyjazdem po drugiej. W tym i kolejnych przewodnikach zakłada się, że miejsca przyjazdu i odjazdu są takie same.

waypoint – przyloty i wyloty to także alternatywa dla linii latLng. Pola Waypoint obsługują używanie identyfikatorów Miejsc Google jako alternatywy dla pola LatLng. Mogą też służyć do określania kierunku pojazdów. Więcej informacji znajdziesz w dokumentacji (REST, gRPC).

Ograniczenia w przykładzie

W tym scenariuszu działanie optymalizatora jest ograniczone na kilka sposobów:

  1. Wszystkie działania muszą zostać ukończone między globalną godziną rozpoczęcia a zakończenia. W tym scenariuszu czasy rozpoczęcia i zakończenia są bardzo restrykcyjne ze względu na bliskość przesyłek i szeroki globalny przedział czasowy.
  2. Wszystkie przesyłki muszą zostać zrealizowane. Jest to działanie domyślne, gdy w dniu shipments koszty karne nie są określone.
  3. W pojeździe ustawiono costPerKilometer i costPerHour.

Informacje na temat kosztów można znaleźć w sekcji Parametry modelu kosztu.

Właściwości odpowiedzi optymalizacji trasy

Zobacz odpowiedź na przykładowe żądanie

    {
      "routes": [
        {
          "vehicleStartTime": "2023-01-14T00:00:00Z",
          "vehicleEndTime": "2023-01-14T00:36:41Z",
          "visits": [
            {
              "shipmentIndex": 2,
              "isPickup": true,
              "startTime": "2023-01-14T00:00:00Z",
              "detour": "0s"
            },
            {
              "shipmentIndex": 1,
              "isPickup": true,
              "startTime": "2023-01-14T00:02:30Z",
              "detour": "150s"
            },
            {
              "isPickup": true,
              "startTime": "2023-01-14T00:05:00Z",
              "detour": "300s"
            },
            {
              "startTime": "2023-01-14T00:11:25Z",
              "detour": "0s"
            },
            {
              "shipmentIndex": 1,
              "startTime": "2023-01-14T00:19:29Z",
              "detour": "503s"
            },
            {
              "shipmentIndex": 2,
              "startTime": "2023-01-14T00:29:02Z",
              "detour": "1324s"
            }
          ],
          "transitions": [
            {
              "travelDuration": "0s",
              "waitDuration": "0s",
              "totalDuration": "0s",
              "startTime": "2023-01-14T00:00:00Z",
              "routePolyline": {}
            },
            {
              "travelDuration": "0s",
              "waitDuration": "0s",
              "totalDuration": "0s",
              "startTime": "2023-01-14T00:02:30Z",
              "routePolyline": {}
            },
            {
              "travelDuration": "0s",
              "waitDuration": "0s",
              "totalDuration": "0s",
              "startTime": "2023-01-14T00:05:00Z",
              "routePolyline": {}
            },
            {
              "travelDuration": "235s",
              "travelDistanceMeters": 795,
              "waitDuration": "0s",
              "totalDuration": "235s",
              "startTime": "2023-01-14T00:07:30Z",
              "routePolyline": {
                "points": "kvteFtfjVAA?C?C@C?A?C@AFMj@s@JKb@k@Zc@LSjA}ARWDGdAxAdAvAXa@@k@AsA\\c@FKp@_A\\c@Ze@fA{ALSFGd@o@rAgBB{BZc@"
              }
            },
            {
              "travelDuration": "234s",
              "travelDistanceMeters": 793,
              "waitDuration": "0s",
              "totalDuration": "234s",
              "startTime": "2023-01-14T00:15:35Z",
              "routePolyline": {
                "points": "cwseFti_jVRWj@w@x@eAHLNRHJbApAHLX\\V^?@hA~AT\\PVFFDHDFJNp@~@NRLNNTFFUZIJY^Y^g@p@[`@KP{@fAEFSXe@l@c@h@WZY\\?BELk@v@MNa@l@"
              }
            },
            {
              "travelDuration": "323s",
              "travelDistanceMeters": 1204,
              "waitDuration": "0s",
              "totalDuration": "323s",
              "startTime": "2023-01-14T00:23:39Z",
              "routePolyline": {
                "points": "cuseFhjVSTY`@Yb@GHEDIJEF]f@IJi@r@oAbBeCfDKLaApAKNQVIPKPCDQJIBIBM@iAJeALqBVC@C?A?QBYDI@C?_@Dc@FO@a@FDp@HfAHvABVDl@Dj@PpCQDiALsALAQASKwAOgBEe@COCYEa@Es@Eg@"
              }
            },
            {
              "travelDuration": "209s",
              "travelDistanceMeters": 665,
              "waitDuration": "0s",
              "totalDuration": "209s",
              "startTime": "2023-01-14T00:33:12Z",
              "routePolyline": {
                "points": "{zteFxbajV?CAYEc@AMC_@AOAK?E?CCWAOAKCe@CY?WScDEm@d@EFA\\ENCB?XEVC^E`@EhBUVCNEB?@?\\Er@IMUe@k@k@w@AAMQa@i@SWQWMQi@u@AC?A"
              }
            }
          ],
          "routePolyline": {
            "points": "kvteFtfjVAA?C?C@C?A?C@AFMj@s@JKb@k@Zc@LSjA}ARWDGdAxAdAvAXa@@k@AsA\\c@FKp@_A\\c@Ze@fA{ALSFGd@o@rAgBB{BZc@RWj@w@x@eAHLNRHJbApAHLX\\V^?@hA~AT\\PVFFDHDFJNp@~@NRLNNTFFUZIJY^Y^g@p@[@KP{@fAEFSXe@l@c@h@WZY\\?BELk@v@MNa@l@STY@Yb@GHEDIJEF]f@IJi@r@oAbBeCfDKLaApAKNQVIPKPCDQJIBIBM@iAJeALqBVC@C?A?QBYDI@C?_@Dc@FO@a@FDp@HfAHvABVDl@Dj@PpCQDiALsALAQASKwAOgBEe@COCYEa@Es@Eg@?CAYEc@AMC_@AOAK?E?CCWAOAKCe@CY?WScDEm@d@EFA\\ENCB?XEVC^E`@EhBUVCNEB?@?\\Er@IMUe@k@k@w@AAMQa@i@SWQWMQi@u@AC?A"
          },
          "metrics": {
            "performedShipmentCount": 3,
            "travelDuration": "1001s",
            "waitDuration": "0s",
            "delayDuration": "0s",
            "breakDuration": "0s",
            "visitDuration": "1200s",
            "totalDuration": "2201s",
            "travelDistanceMeters": 3457
          },
          "travelSteps": [
            {
              "duration": "0s",
              "routePolyline": {}
            },
            {
              "duration": "0s",
              "routePolyline": {}
            },
            {
              "duration": "0s",
              "routePolyline": {}
            },
            {
              "duration": "227s",
              "distanceMeters": 794,
              "routePolyline": {
                "points": "kvteFtfjVAA?C?C@C?A?C@AFMj@s@JKb@k@Zc@LSjA}ARWDGdAxAdAvAXa@@k@AsA\\c@FKp@_A\\c@Ze@fA{ALSFGd@o@rAgBB{BZc@"
              }
            },
            {
              "duration": "233s",
              "distanceMeters": 791,
              "routePolyline": {
                "points": "cwseFti_jVRWj@w@x@eAHLNRHJbApAHLX\\V^?@hA~AT\\PVFFDHDFJNp@~@NRLNNTFFUZIJY^Y^g@p@[`@KP{@fAEFSXe@l@c@h@WZY\\?BELk@v@MNa@l@"
              }
            },
            {
              "duration": "322s",
              "distanceMeters": 1205,
              "routePolyline": {
                "points": "cuseFhjVSTY`@Yb@GHEDIJEF]f@IJi@r@oAbBeCfDKLaApAKNQVIPKPCDQJIBIBM@iAJeALqBVC@C?A?QBYDI@C?_@Dc@FO@a@FDp@HfAHvABVDl@Dj@PpCQDiALsALAQASKwAOgBEe@COCYEa@Es@Eg@"
              }
            },
            {
              "duration": "208s",
              "distanceMeters": 666,
              "routePolyline": {
                "points": "{zteFxbajV?CAYEc@AMC_@AOAK?E?CCWAOAKCe@CY?WScDEm@d@EFA\\ENCB?XEVC^E`@EhBUVCNEB?@?\\Er@IMUe@k@k@w@AAMQa@i@SWQWMQi@u@AC?A"
              }
            }
          ],
          "vehicleDetour": "2201s",
          "routeCosts": {
            "model.vehicles.cost_per_hour": 24.455555555555556,
            "model.vehicles.cost_per_kilometer": 34.57
          },
          "routeTotalCost": 59.025555555555556
        }
      ],
      "totalCost": 59.025555555555556,
      "metrics": {
        "aggregatedRouteMetrics": {
          "performedShipmentCount": 3,
          "travelDuration": "1001s",
          "waitDuration": "0s",
          "delayDuration": "0s",
          "breakDuration": "0s",
          "visitDuration": "1200s",
          "totalDuration": "2201s",
          "travelDistanceMeters": 3457
        },
        "usedVehicleCount": 1,
        "earliestVehicleStartTime": "2023-01-14T00:00:00Z",
        "latestVehicleEndTime": "2023-01-14T00:36:41Z",
        "totalCost": 59.025555555555556,
        "costs": {
          "model.vehicles.cost_per_kilometer": 34.57,
          "model.vehicles.cost_per_hour": 24.455555555555556
        }
      }
    }
    

Odpowiedź dotycząca optymalizacji trasy zawiera pole routes najwyższego poziomu, które reprezentuje proponowane trasy z 1 trasą na pojazd. Przykładowe żądanie w tym przewodniku wskazuje tylko 1 pojazd, więc routes zawiera 1 komunikat ShipmentRoute.

ShipmentRoute miejsca zakwaterowania

Dwie najważniejsze właściwości typu wiadomości ShipmentRoute to visits i transitions.

Każdy element Visit oznacza zakończenie odbioru lub dostawy w jednym z elementów VisitRequest wiadomości. Wizyta to tak zwane zadanie, które pojazd może wykonać w określonym miejscu i o czasie.

Każdy Transition oznacza pojazd podróżujący z jednego miejsca do następnego. Przejścia mogą wystąpić między parą punktu początkowego pojazdu, lokalizacji wizyty i punktu końcowego pojazdu.

Aby można było odtworzyć pełną trasę pojazdu, visits i transitions pojazdu ShipmentRoute muszą być połączone. Połączenie pól w postęp działań związanych z pojazdem wygląda tak:

request.vehicles[0].startLocation -> transitions[0] -> visits[0] ->
transitions[1] -> visits[1] -> transitions[2] -> ... -> visits[3] ->
transitions[4] -> request.vehicles[0].endLocation

Samochód ShipmentRoute ma zawsze o jeden więcej transitions niż visits, ponieważ pojazd musi pokonać trasę od miejsca początkowego do pierwszego przejazdu na początku trasy oraz od ostatniego wizyty do miejsca zakończenia na końcu trasy. Jeśli pojazd nie ma określonej lokalizacji początkowej lub końcowej, nadal będzie mieć o 1 więcej wyświetleń transitions niż visits, ponieważ lokalizacja początkowa lub końcowa będzie używana odpowiednio jako lokalizacja początkowa lub końcowa.

W tym przykładzie pierwsze 3 wizyty z odbiorem przechodzą między nimi przy zerowej odległości i czasie trwania, ponieważ wszystkie 3 odbiory mają tę samą lokalizację w żądaniu.

Więcej informacji znajdziesz w dokumentacji referencyjnej ShipmentRoute (REST, gRPC).

Prosta optymalizacja kolejności punktów pośrednich

Jak pokazujemy w tym przykładzie, optymalizacja tras modeluje wizyty jako właściwości przesyłki i nie uwzględnia punktów pośrednich ani przystanków jako niezależnych elementów. Można jednak przedstawić przystanki lub punkty pośrednie jako przesyłki z dokładnie jednym elementem VisitRequest jako odbiorem lub dostawą. Pojazd musi mieć przypisany costPerHour lub costPerKilometer, aby optymalizator mógł znaleźć optymalną trasę (a nie możliwą trasę).