수령 및 배송 기간 제한

OptimizeToursRequest는 다음에 제약 조건을 적용합니다.

  • 배송 - 배송이 실행되는 방식에 영향을 미칩니다.
  • 차량 경로 계산 방식에 영향을 미치는 차량
  • 전 세계적으로 차량과 배송에 모두 영향을 미칩니다.

이 가이드에서는 필수 배송 제약조건인 시간 범위에 중점을 둡니다.

기간은 배송 활동에 시간 기반 제한을 지정하기 위해 OptimizeToursRequest 메시지(REST, gRPC)에 제공하는 한 가지 유형의 제약 조건입니다. 이러한 유형의 제약 조건은 배송이 가능한 시기 및 방법과 차량 할당 합니다. 이러한 제약조건을 토대로 최적화 도구는 배송의 시간 제약조건을 가장 잘 충족할 수 있는 차량을 우선시합니다.

배송 제약조건: 기간

다음과 같이 Shipment.VisitRequest 메시지에서 수령 또는 배송이 발생할 수 있는 시점을 지정합니다.

  • 메시지에서 timeWindows 속성 사용 (REST, gRPC)
  • TimeWindow 메시지 (REST, gRPC)를 사용합니다.

시간 제한이 있는 요청 예시

이 예에서는 각각 배송 기간이 다른 세 가지 배송을 보여줍니다. 편의상 이 예에서는 deliveries에만 시간대를 설정하지만 시간대는 픽업에도 적용할 수 있습니다. 기간이 여러 개인 경우 이 예에서는 전송 VisitRequest당 하나만 사용합니다.

시간대가 있는 요청 예 보기

{
  "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",
            "timeWindows": [
              {
                "startTime": "2023-01-13T18:00:00Z",
                "endTime": "2023-01-13T19:00:00Z"
              }
            ]
          }
        ],
        "pickups": [
          {
            "arrivalLocation": {
              "latitude": 37.794465,
              "longitude": -122.394839
            },
            "duration": "150s"
          }
        ],
        "penaltyCost": 100.0
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 37.789116,
              "longitude": -122.395080
            },
            "duration": "250s",
            "timeWindows": [
              {
                "startTime": "2023-01-13T18:00:00Z",
                "endTime": "2023-01-13T18:30:00Z"
              }
            ]
          }
        ],
        "pickups": [
          {
            "arrivalLocation": {
              "latitude": 37.794465,
              "longitude": -122.394839
            },
            "duration": "150s"
          }
        ],
        "penaltyCost": 20.0
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 37.795242,
              "longitude": -122.399347
            },
            "duration": "250s",
            "timeWindows": [
              {
                "startTime": "2023-01-13T17:30:00Z",
                "endTime": "2023-01-13T18:00:00Z"
              }
            ]
          }
        ],
        "pickups": [
          {
            "arrivalLocation": {
              "latitude": 37.794465,
              "longitude": -122.394839
            },
            "duration": "150s"
          }
        ],
        "penaltyCost": 50.0
      }
    ],
    "vehicles": [
      {
        "endLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "startLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "costPerHour": 40.0,
        "costPerKilometer": 10.0
      }
    ]
  }
}
    

시간 제한이 있는 응답 예시

응답 예시에서 차량의 시작 시간과 종료 시간은 각각 17:35:50 및 18:17:24입니다. 이 시간은 옵티마이저가 시간을 최소화하는 것을 요청에 costPerHour로 지정된 차량을 작동하는 데 필요한 모든 기간 제약 조건을 충족해야 합니다 17:35:50을 시작 시간으로 사용하면 방문 시간대가 시작될 때까지 차량이 방문 위치에서 기다릴 필요가 없습니다. 이는 응답에 0 waitDuration 값으로 표시됩니다.

다음 명령어로 예시 요청에 대한 응답을 확인합니다. 기간

{
  "routes": [
    {
      "vehicleStartTime": "2023-01-13T17:35:50Z",
      "vehicleEndTime": "2023-01-13T18:17:24Z",
      "visits": [
        {
          "isPickup": true,
          "startTime": "2023-01-13T17:35:50Z",
          "detour": "0s"
        },
        {
          "shipmentIndex": 1,
          "isPickup": true,
          "startTime": "2023-01-13T17:38:20Z",
          "detour": "150s"
        },
        {
          "shipmentIndex": 2,
          "isPickup": true,
          "startTime": "2023-01-13T17:40:50Z",
          "detour": "300s"
        },
        {
          "shipmentIndex": 2,
          "startTime": "2023-01-13T17:50:09Z",
          "detour": "0s"
        },
        {
          "shipmentIndex": 1,
          "startTime": "2023-01-13T18:00:00Z",
          "detour": "796s"
        },
        {
          "startTime": "2023-01-13T18:07:35Z",
          "detour": "1520s"
        }
      ],
      "transitions": [
        {
          "travelDuration": "0s",
          "waitDuration": "0s",
          "totalDuration": "0s",
          "startTime": "2023-01-13T17:35:50Z"
        },
        {
          "travelDuration": "0s",
          "waitDuration": "0s",
          "totalDuration": "0s",
          "startTime": "2023-01-13T17:38:20Z"
        },
        {
          "travelDuration": "0s",
          "waitDuration": "0s",
          "totalDuration": "0s",
          "startTime": "2023-01-13T17:40:50Z"
        },
        {
          "travelDuration": "409s",
          "travelDistanceMeters": 1371,
          "waitDuration": "0s",
          "totalDuration": "409s",
          "startTime": "2023-01-13T17:43:20Z"
        },
        {
          "travelDuration": "341s",
          "travelDistanceMeters": 1312,
          "waitDuration": "0s",
          "totalDuration": "341s",
          "startTime": "2023-01-13T17:54:19Z"
        },
        {
          "travelDuration": "205s",
          "travelDistanceMeters": 636,
          "waitDuration": "0s",
          "totalDuration": "205s",
          "startTime": "2023-01-13T18:04:10Z"
        },
        {
          "travelDuration": "339s",
          "travelDistanceMeters": 1276,
          "waitDuration": "0s",
          "totalDuration": "339s",
          "startTime": "2023-01-13T18:11:45Z"
        }
      ],
      "metrics": {
        "performedShipmentCount": 3,
        "travelDuration": "1294s",
        "waitDuration": "0s",
        "delayDuration": "0s",
        "breakDuration": "0s",
        "visitDuration": "1200s",
        "totalDuration": "2494s",
        "travelDistanceMeters": 4595
      },
      "routeCosts": {
        "model.vehicles.cost_per_hour": 27.711111111111112,
        "model.vehicles.cost_per_kilometer": 45.95
      },
      "routeTotalCost": 73.661111111111111
    }
  ],
  "metrics": {
    "aggregatedRouteMetrics": {
      "performedShipmentCount": 3,
      "travelDuration": "1294s",
      "waitDuration": "0s",
      "delayDuration": "0s",
      "breakDuration": "0s",
      "visitDuration": "1200s",
      "totalDuration": "2494s",
      "travelDistanceMeters": 4595
    },
    "usedVehicleCount": 1,
    "earliestVehicleStartTime": "2023-01-13T17:35:50Z",
    "latestVehicleEndTime": "2023-01-13T18:17:24Z",
    "totalCost": 73.661111111111111,
    "costs": {
      "model.vehicles.cost_per_hour": 27.711111111111112,
      "model.vehicles.cost_per_kilometer": 45.95
    }
  }
}
    

가장 빠른 기간이 있는 배송이 먼저 배송되도록 기간이 차량의 visits를 정렬했습니다.

  1. shipments[2] 배송 예정 시간: 17:50
  2. shipments[1]은 18:00에 배송됩니다.
  3. shipments[0]이 18:07에 전송됨

요청 예에는 하드 기간 제약 조건이 지정되어 있으며, 배송을 완료해야 합니다. 배송 상품의 기간 중 VisitRequests을(를) 실행할 수 없거나 최적화 도구는 배송을 건너뜁니다. 배송에 penaltyCost가 있는 경우 최적화 도구는 metrics 응답에 보고된 비용에 이를 추가합니다. 그렇지 않으면 OptimizeToursResponse 메시지(REST, gRPC)의 skippedMandatoryShipmentCount 속성이 증가합니다.

shipment[1]의 시간대를 몇 시간 후에 변경하여(18:00에서 21:00으로) 시간대를 변경하면 다음 예와 같이 결과가 달라집니다.

충족할 수 없는 시간 기간이 있는 요청 예시 보기

{
  "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",
            "timeWindows": [
              {
                "startTime": "2023-01-13T18:00:00Z",
                "endTime": "2023-01-13T19:00:00Z"
              }
            ]
          }
        ],
        "pickups": [
          {
            "arrivalLocation": {
              "latitude": 37.794465,
              "longitude": -122.394839
            },
            "duration": "150s"
          }
        ],
        "penaltyCost": 100.0
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 37.789116,
              "longitude": -122.395080
            },
            "duration": "250s",
            "timeWindows": [
              {
                "startTime": "2023-01-13T21:00:00Z",
                "endTime": "2023-01-13T21:30:00Z"
              }
            ]
          }
        ],
        "pickups": [
          {
            "arrivalLocation": {
              "latitude": 37.794465,
              "longitude": -122.394839
            },
            "duration": "150s"
          }
        ],
        "penaltyCost": 20.0
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 37.795242,
              "longitude": -122.399347
            },
            "duration": "250s",
            "timeWindows": [
              {
                "startTime": "2023-01-13T17:30:00Z",
                "endTime": "2023-01-13T18:00:00Z"
              }
            ]
          }
        ],
        "pickups": [
          {
            "arrivalLocation": {
              "latitude": 37.794465,
              "longitude": -122.394839
            },
            "duration": "150s"
          }
        ],
        "penaltyCost": 50.0
      }
    ],
    "vehicles": [
      {
        "endLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "startLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "costPerHour": 40.0,
        "costPerKilometer": 10.0
      }
    ]
  }
}
    

배송이 건너뛴 시간 범위가 있는 두 번째 예시 요청에 대한 응답 보기

{
  "routes": [
    {
      "vehicleStartTime": "2023-01-13T17:37:49Z",
      "vehicleEndTime": "2023-01-13T18:09:49Z",
      "visits": [
        {
          "isPickup": true,
          "startTime": "2023-01-13T17:37:49Z",
          "detour": "0s"
        },
        {
          "shipmentIndex": 2,
          "isPickup": true,
          "startTime": "2023-01-13T17:40:19Z",
          "detour": "150s"
        },
        {
          "shipmentIndex": 2,
          "startTime": "2023-01-13T17:49:38Z",
          "detour": "0s"
        },
        {
          "startTime": "2023-01-13T18:00:00Z",
          "detour": "946s"
        }
      ],
      "transitions": [
        {
          "travelDuration": "0s",
          "waitDuration": "0s",
          "totalDuration": "0s",
          "startTime": "2023-01-13T17:37:49Z"
        },
        {
          "travelDuration": "0s",
          "waitDuration": "0s",
          "totalDuration": "0s",
          "startTime": "2023-01-13T17:40:19Z"
        },
        {
          "travelDuration": "409s",
          "travelDistanceMeters": 1371,
          "waitDuration": "0s",
          "totalDuration": "409s",
          "startTime": "2023-01-13T17:42:49Z"
        },
        {
          "travelDuration": "372s",
          "travelDistanceMeters": 1348,
          "waitDuration": "0s",
          "totalDuration": "372s",
          "startTime": "2023-01-13T17:53:48Z"
        },
        {
          "travelDuration": "339s",
          "travelDistanceMeters": 1276,
          "waitDuration": "0s",
          "totalDuration": "339s",
          "startTime": "2023-01-13T18:04:10Z"
        }
      ],
      "metrics": {
        "performedShipmentCount": 2,
        "travelDuration": "1120s",
        "waitDuration": "0s",
        "delayDuration": "0s",
        "breakDuration": "0s",
        "visitDuration": "800s",
        "totalDuration": "1920s",
        "travelDistanceMeters": 3995
      },
      "routeCosts": {
        "model.vehicles.cost_per_kilometer": 39.95,
        "model.vehicles.cost_per_hour": 21.333333333333332
      },
      "routeTotalCost": 61.283333333333331
    }
  ],
  "skippedShipments": [
    {
      "index": 1
    }
  ],
  "metrics": {
    "aggregatedRouteMetrics": {
      "performedShipmentCount": 2,
      "travelDuration": "1120s",
      "waitDuration": "0s",
      "delayDuration": "0s",
      "breakDuration": "0s",
      "visitDuration": "800s",
      "totalDuration": "1920s",
      "travelDistanceMeters": 3995
    },
    "usedVehicleCount": 1,
    "earliestVehicleStartTime": "2023-01-13T17:37:49Z",
    "latestVehicleEndTime": "2023-01-13T18:09:49Z",
    "totalCost": 81.283333333333331,
    "costs": {
      "model.shipments.penalty_cost": 20,
      "model.vehicles.cost_per_hour": 21.333333333333332,
      "model.vehicles.cost_per_kilometer": 39.95
    }
  }
}
    

이 예시에서는 이후 기간으로 인해 shipment[1]를 건너뛰었습니다. 배송을 완료하는 데 필요한 추가 차량 운행 시간이 지정된 기간 내에 배송 건이 배송 위약금을 초과했습니다. shipment[1]의 페널티 비용은 metrics.costs 및 색인에 표시됩니다. skippedShipments에 나타납니다.

소프트 타임 제약 조건

비용 모델 매개변수에서 간단히 설명했듯이 기간을 적용할 수 있습니다. soft 제약 조건으로 사용됩니다. 소프트 제약 조건은 다음과 같이 하드 제약 조건과 다릅니다.

  • 엄격한 제약 조건: 위반해서는 안 되며 최적화 도구에서 이는 제약 조건을 위반하는 솔루션이며, 이는 배송을 탭합니다.
  • 소프트 제약 조건: 위반할 수 있습니다. 즉, 최적화 도구에서 유연한 제약 조건을 위반하는 솔루션을 제공합니다. 하지만 옵티마이저가 위반 시 비용도 부과됩니다. 이 비용은 시간 범위의 추가 속성으로 제공되며 일반적으로 활동이 발생하는 시간 범위 전후에 각 시간의 시간당 비용으로 제공됩니다.

startTime 또는 endTime 대신 softStartTime 또는 softEndTime를 사용하고 costPerHourBeforeSoftStartTime 또는 costPerHourAfterSoftEndTime를 설정하면 시간 창이 완화됩니다.

수령 또는 배달이 발생해야 하는 경우 비고정 시간대 제약조건을 사용하세요. 배송 상품을 수령 또는 배송하는 경우 반드시 필요합니다 하드 타임 윈도우 제약 조건과 소프트 타임 윈도우 제약 조건을 함께 사용하여 비즈니스 목표를 표현할 수 있습니다. 예를 들면 다음과 같습니다.

  • 고정 시간 범위: 오전 9시~오후 5시와 같이 고객의 영업시간을 나타냅니다.
  • 비공식 시간대: 특정 상품의 배송 또는 수령 시간을 나타냅니다. 고객에게 전송되는 알림과 일치합니다(예: 오전 9시~오후 1시).

이 예에서는 시간 창이 너무 늦게 시작되어 이전에 건너뛴 배송의 시작 시간 제약 조건이 완화되었습니다. 다른 배송의 경우에도 기간 종료 시간이 완화되었습니다.

하드 타임 및 소프트 타임 요청 예시 보기 창문

{
  "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",
            "timeWindows": [
              {
                "startTime": "2023-01-13T18:00:00Z",
                "softEndTime": "2023-01-13T19:00:00Z",
                "costPerHourAfterSoftEndTime": 2.0
              }
            ]
          }
        ],
        "pickups": [
          {
            "arrivalLocation": {
              "latitude": 37.794465,
              "longitude": -122.394839
            },
            "duration": "150s"
          }
        ],
        "penaltyCost": 100.0
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 37.789116,
              "longitude": -122.395080
            },
            "duration": "250s",
            "timeWindows": [
              {
                "softStartTime": "2023-01-13T21:00:00Z",
                "endTime": "2023-01-13T21:30:00Z",
                "costPerHourBeforeSoftStartTime": 2.0
              }
            ]
          }
        ],
        "pickups": [
          {
            "arrivalLocation": {
              "latitude": 37.794465,
              "longitude": -122.394839
            },
            "duration": "150s"
          }
        ],
        "penaltyCost": 20.0
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 37.795242,
              "longitude": -122.399347
            },
            "duration": "250s",
            "timeWindows": [
              {
                "startTime": "2023-01-13T17:30:00Z",
                "softEndTime": "2023-01-13T18:00:00Z",
                "costPerHourAfterSoftEndTime": 2.0
              }
            ]
          }
        ],
        "pickups": [
          {
            "arrivalLocation": {
              "latitude": 37.794465,
              "longitude": -122.394839
            },
            "duration": "150s"
          }
        ],
        "penaltyCost": 50.0
      }
    ],
    "vehicles": [
      {
        "endLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "startLocation": {
          "latitude": 37.794465,
          "longitude": -122.394839
        },
        "costPerHour": 40.0,
        "costPerKilometer": 10.0
      }
    ]
  }
}
    

하드 및 소프트 타임 기간

{
  "routes": [
    {
      "vehicleStartTime": "2023-01-13T17:48:35Z",
      "vehicleEndTime": "2023-01-13T18:24:28Z",
      "visits": [
        {
          "isPickup": true,
          "startTime": "2023-01-13T17:48:35Z",
          "detour": "0s"
        },
        {
          "shipmentIndex": 1,
          "isPickup": true,
          "startTime": "2023-01-13T17:51:05Z",
          "detour": "150s"
        },
        {
          "shipmentIndex": 2,
          "isPickup": true,
          "startTime": "2023-01-13T17:53:35Z",
          "detour": "300s"
        },
        {
          "startTime": "2023-01-13T18:00:00Z",
          "detour": "300s"
        },
        {
          "shipmentIndex": 1,
          "startTime": "2023-01-13T18:07:42Z",
          "detour": "493s"
        },
        {
          "shipmentIndex": 2,
          "startTime": "2023-01-13T18:17:27Z",
          "detour": "873s"
        }
      ],
      "transitions": [
        {
          "travelDuration": "0s",
          "waitDuration": "0s",
          "totalDuration": "0s",
          "startTime": "2023-01-13T17:48:35Z"
        },
        {
          "travelDuration": "0s",
          "waitDuration": "0s",
          "totalDuration": "0s",
          "startTime": "2023-01-13T17:51:05Z"
        },
        {
          "travelDuration": "0s",
          "waitDuration": "0s",
          "totalDuration": "0s",
          "startTime": "2023-01-13T17:53:35Z"
        },
        {
          "travelDuration": "235s",
          "travelDistanceMeters": 795,
          "waitDuration": "0s",
          "totalDuration": "235s",
          "startTime": "2023-01-13T17:56:05Z"
        },
        {
          "travelDuration": "212s",
          "travelDistanceMeters": 791,
          "waitDuration": "0s",
          "totalDuration": "212s",
          "startTime": "2023-01-13T18:04:10Z"
        },
        {
          "travelDuration": "335s",
          "travelDistanceMeters": 1204,
          "waitDuration": "0s",
          "totalDuration": "335s",
          "startTime": "2023-01-13T18:11:52Z"
        },
        {
          "travelDuration": "171s",
          "travelDistanceMeters": 665,
          "waitDuration": "0s",
          "totalDuration": "171s",
          "startTime": "2023-01-13T18:21:37Z"
        }
      ],
      "metrics": {
        "performedShipmentCount": 3,
        "travelDuration": "953s",
        "waitDuration": "0s",
        "delayDuration": "0s",
        "breakDuration": "0s",
        "visitDuration": "1200s",
        "totalDuration": "2153s",
        "travelDistanceMeters": 3455
      },
      "routeCosts": {
        "model.shipments.deliveries.time_windows.cost_per_hour_after_soft_end_time": 0.58166666666666667,
        "model.shipments.deliveries.time_windows.cost_per_hour_before_soft_start_time": 5.7433333333333332,
        "model.vehicles.cost_per_hour": 23.922222222222221,
        "model.vehicles.cost_per_kilometer": 34.55
      },
      "routeTotalCost": 64.797222222222217
    }
  ],
  "metrics": {
    "aggregatedRouteMetrics": {
      "performedShipmentCount": 3,
      "travelDuration": "953s",
      "waitDuration": "0s",
      "delayDuration": "0s",
      "breakDuration": "0s",
      "visitDuration": "1200s",
      "totalDuration": "2153s",
      "travelDistanceMeters": 3455
    },
    "usedVehicleCount": 1,
    "earliestVehicleStartTime": "2023-01-13T17:48:35Z",
    "latestVehicleEndTime": "2023-01-13T18:24:28Z",
    "totalCost": 64.797222222222217,
    "costs": {
      "model.vehicles.cost_per_kilometer": 34.55,
      "model.shipments.deliveries.time_windows.cost_per_hour_before_soft_start_time": 5.7433333333333332,
      "model.shipments.deliveries.time_windows.cost_per_hour_after_soft_end_time": 0.58166666666666667,
      "model.vehicles.cost_per_hour": 23.922222222222221
    }
  }
}
    

엄격한 시간 제약 조건만 있는 예에서는 shipment[1]가 완전히 건너뛰었지만, 전송 시간 제한을 완화하면 시간 제한 시작 시간 전에 전송됩니다. 마찬가지로 다른 배송 상품으로 인해 shipment[2] 상품을 해당 기간 이후 배송 가능 종료.

동시에 배송비와 총 배송량도 다음과 같이 변경되었습니다.

  • totalCost: 81.283에서 64.797로 감소
  • 완료된 총 배송 수: 2건에서 3건으로 증가했습니다.

옵티마이저가 비용이 더 적게 드는 솔루션을 찾았습니다. 이전 예에 비해 제약이 완화되었습니다.

마지막으로 metrics.costs 속성에는 제약 조건과 전송 기간을 놓친 시간을 곱한 값을 기반으로 발생한 실제 비용을 나타내는 새 키도 포함됩니다. 이는 다음과 같은 의미입니다.

  • 2.0의 costPerHourBeforeSoftStartTime
  • 실제 전송과 시간 범위 시작 사이의 시간: 2.83583시간

결과:

model.shipments.deliveries.time_windows.cost_per_hour_before_soft_start_time: 5.6716666666666669).

이러한 측정항목을 사용하면 비용 분석을 통해 하드 제약조건과 소프트 제약조건 간의 균형을 확인할 수 있으며, 이를 사용하여 특정 비즈니스 규칙에 더 적합하도록 제약조건을 조정할 수 있습니다. 이 경우 총 비용은 shipment[1].penalty_cost 20.0보다 작습니다. 최적화 도구에서 배송하는 것이 실제 배송보다 비용 효율적임을 배송을 건너뛸 수 있습니다.