Tối ưu hoá đơn hàng dừng cơ bản cho hoạt động đến lấy hàng và giao hàng

Tình huống này tối ưu hoá thứ tự các điểm dừng được chỉ định cho một chiếc xe bằng các thông số chi phí đơn giản. Đây là chế độ đơn giản nhất của hoạt động Tối ưu hoá tuyến đường và đảm bảo rằng tất cả các điểm dừng đều được ghé thăm trong khung thời gian đã chỉ định.

Ví dụ sau đây minh hoạ một kịch bản cơ bản với một xe và 3 lô hàng, tất cả đều xuất phát từ một vị trí duy nhất được gọi là kho.

Xem yêu cầu mẫu

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

Các trường yêu cầu Tối ưu hoá tuyến đường

Như đã đề cập trong phần Tổng quan, các thuộc tính yêu cầu quan trọng nhất của Dịch vụ tối ưu hoá tuyến đường là vehiclesshipments.

Ngoài xe và lô hàng, yêu cầu này còn có các trường sau:

Đường đa tuyến

populatePolylinespopulateTransitionPolylines chỉ định xem RouteOptimization có trả về nhiều đường hay không.

Dịch vụ này mã hoá Polyline bằng bộ mã hoá polyline Maps JS, biểu thị dữ liệu polyline nhị phân bằng các ký tự ASCII có thể in. Bạn có thể sử dụng Tiện ích mã hoá đường nhiều đoạn tương tác để trực quan hoá các đường dẫn do Dịch vụ tối ưu hoá tuyến đường tính toán. Ví dụ trong hướng dẫn này đặt populatePolylinespopulateTransitionPolylines thành true, nhưng các hướng dẫn khác đặt chúng thành false để giảm kích thước phản hồi.

Hãy xem Định dạng thuật toán đường nhiều đoạn được mã hoá để biết nội dung mô tả về định dạng mã hoá.

Hạn chế về thời gian trên toàn cầu

model.globalStartTimemodel.globalEndTime được đặt thành một khoảng thời gian tuỳ ý là 24 giờ. Điều này giúp bạn dễ dàng diễn giải dấu thời gian đầu ra.

Ghé thăm các địa điểm

Yêu cầu minh hoạ chỉ sử dụng model.shipments[].pickups[].arrivalLocationmodel.shipments[].deliveries[].arrivalLocation. Ngoài ra, còn có một thuộc tính departureLocation cho những trường hợp xe rời đi từ một điểm khác với điểm đến, chẳng hạn như một khu phức hợp bãi đỗ xe có lối vào ở một bên của toà nhà và lối ra ở bên còn lại. Trong hướng dẫn này và các hướng dẫn tiếp theo, chúng tôi giả định rằng điểm đến và điểm đi là như nhau.

Ngoài ra, bạn cũng có thể dùng waypoint để thay thế cho latLng. Các trường Waypoint hỗ trợ việc sử dụng mã địa điểm của Google thay cho LatLng, đồng thời cũng có thể chỉ định hướng di chuyển của xe. Hãy xem tài liệu tham khảo (REST, gRPC) để biết thêm thông tin chi tiết.

Các ràng buộc trong ví dụ

Tình huống này hạn chế trình tối ưu hoá theo một số cách:

  1. Bạn phải hoàn thành tất cả hoạt động trong khoảng thời gian bắt đầu và kết thúc trên toàn cầu. Trong trường hợp này, thời gian bắt đầu và thời gian kết thúc là một điều kiện ràng buộc rất lỏng lẻo do khoảng cách gần giữa các lô hàng và khung thời gian toàn cầu rộng.
  2. Bạn phải hoàn tất tất cả các lô hàng. Đây là hành vi mặc định khi bạn không chỉ định chi phí phạt trên shipments.
  3. costPerKilometercostPerHour được đặt trên xe.

Chi phí được đề cập trong phần Tham số mô hình chi phí.

Các thuộc tính phản hồi của tính năng Tối ưu hoá tuyến đường

Xem phản hồi cho yêu cầu ví dụ

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

Phản hồi của API Tối ưu hoá tuyến đường bao gồm một trường routes cấp cao nhất đại diện cho các tuyến đường được đề xuất, với một tuyến đường cho mỗi xe. Vì yêu cầu mẫu trong hướng dẫn này chỉ định một xe, nên routes bao gồm một thông báo ShipmentRoute.

ShipmentRoute cơ sở lưu trú

Hai thuộc tính quan trọng nhất cho loại thông báo ShipmentRoutevisitstransitions.

Mỗi Visit đại diện cho việc hoàn tất một lần đến lấy hàng hoặc giao hàng từ một trong các VisitRequest của thông báo yêu cầu. Lượt ghé thăm là công việc được giao cho một chiếc xe hoàn thành tại một địa điểm và thời gian nào đó.

Mỗi Transition đại diện cho chiếc xe di chuyển từ vị trí này đến vị trí tiếp theo. Quá trình chuyển đổi có thể xảy ra giữa một cặp điểm bắt đầu của xe, vị trí ghé thăm và điểm kết thúc của xe.

Để tái tạo toàn bộ tuyến đường của xe, bạn phải kết hợp visitstransitions của ShipmentRoute. Sự kết hợp các trường thành một chuỗi hoạt động của xe sẽ có dạng như sau:

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

ShipmentRoute luôn có nhiều hơn transitions một visits, vì xe phải di chuyển từ vị trí bắt đầu đến lượt ghé thăm đầu tiên ở đầu tuyến đường và từ lượt ghé thăm cuối cùng đến vị trí kết thúc ở cuối tuyến đường. Nếu xe không có vị trí bắt đầu hoặc kết thúc, thì vẫn sẽ có nhiều hơn 1 transitions so với visits vì vị trí của lượt ghé thăm đầu tiên hoặc cuối cùng được dùng làm vị trí bắt đầu hoặc kết thúc của xe tương ứng.

Trong ví dụ này, 3 lượt ghé qua đầu tiên để lấy hàng có các lượt chuyển đổi giữa chúng với khoảng cách và thời lượng bằng 0 vì cả 3 lượt lấy hàng đều có cùng một vị trí trong yêu cầu.

Hãy xem tài liệu tham khảo ShipmentRoute (REST, gRPC) để biết thêm thông tin chi tiết.

Đơn giản hoá việc tối ưu hoá thứ tự điểm tham chiếu

Như ví dụ này minh hoạ, tính năng Tối ưu hoá tuyến đường mô hình hoá các lượt ghé thăm dưới dạng thuộc tính của lô hàng và không có khái niệm về điểm tham chiếu hoặc điểm dừng dưới dạng một thực thể độc lập. Tuy nhiên, bạn có thể biểu thị các điểm dừng hoặc điểm tham chiếu dưới dạng lô hàng có chính xác một VisitRequest là điểm lấy hàng hoặc giao hàng. Xe vẫn phải được chỉ định costPerHour hoặc costPerKilometer để trình tối ưu hoá tìm được tuyến đường tối ưu (thay vì tìm bất kỳ tuyến đường khả thi nào).