OptimizeToursRequest
aplica restricciones en los siguientes elementos:
- Envíos, lo que afecta la forma en que se realizan los envíos
- Vehículos, lo que afecta la forma en que se calculan las rutas
- A nivel global, y afecta tanto a los vehículos como a los envíos.
Esta guía se centra en una restricción de envío esencial: los períodos.
Los períodos son un tipo de restricción que proporcionas en el
Mensaje OptimizeToursRequest
(REST, gRPC) para especificar
límites basados en el tiempo para las actividades de envío. Este tipo de restricción influye
tanto cuándo y cómo se puede realizar un envío, así como la asignación del vehículo
para el envío. Con estas restricciones, el optimizador da preferencia
aquellos vehículos que puedan satisfacer mejor las limitaciones de tiempo del envío.
Restricciones de envío: períodos
Puedes especificar cuándo puede realizarse un retiro o una entrega en el Shipment.VisitRequest
mensaje de la siguiente manera:
- Usa la propiedad
timeWindows
en el mensaje (REST, gRPC). - Especifica la hora de inicio y finalización en el mensaje
TimeWindow
(REST, gRPC).
Ejemplo de solicitud con restricciones de tiempo
En este ejemplo, se ilustran tres envíos diferentes, cada uno con su
plazo de entrega. Para simplificar, en este ejemplo, se configuran ventanas de tiempo en deliveries
solamente, pero los períodos también se pueden aplicar a los puntos de partida. Varios períodos pueden
pero en este ejemplo solo se usa uno por entrega VisitRequest
.
Ver una solicitud de ejemplo con períodos
{ "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 } ] } }
Respuesta de ejemplo con restricciones de período
En la respuesta de ejemplo, las horas de inicio y finalización del vehículo son 17:35:50 y
18:17:24, respectivamente. Estos tiempos reflejan que el optimizador minimiza el tiempo
necesario para operar el vehículo especificado en la solicitud como costPerHour
mientras
que satisfaga todas las restricciones de plazos. Usar 17:35:50 como hora de inicio
elimina la necesidad de que el vehículo espere en el lugar de una visita hasta que
comienza el período de la visita. Esto aparece en la respuesta como cero waitDuration
de salida.
Ver una respuesta a la solicitud de ejemplo con períodos
{ "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 } } }
Los intervalos de tiempo ordenaron el visits
del vehículo para que los envíos con la
los horarios más tempranos se muestran primero.
shipments[2]
se entrega a las 5:50 p.m.shipments[1]
se entrega a las 6:00 p.m.shipments[0]
se entrega a las 6:07 p.m.
La solicitud de ejemplo especifica restricciones de período difícil, que requieren
que las entregas se completen dentro de esas ventanas. Si completas el
VisitRequests
en ninguna de sus ventanas es factible o
rentable, el optimizador omite el envío. Si el envío tiene un
penaltyCost
, el optimizador lo agrega a los costos informados como respuesta.
metrics
De lo contrario, la propiedad skippedMandatoryShipmentCount
de la
Aumenta el mensaje OptimizeToursResponse
(REST, gRPC).
Si cambias las ventanas de tiempo cambiando la ventana de shipment[1]
varias horas
más tarde (hasta las 21:00 de las 18:00), los resultados serán distintos como se ilustra en el
siguientes ejemplos.
Ver una solicitud de ejemplo con períodos que no se pueden estar satisfecho
{ "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 } ] } }
Ve una respuesta a la segunda solicitud de ejemplo con períodos en los que se omite un envío
{ "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 } } }
En este ejemplo, el período posterior hizo que se omitiera shipment[1]
.
ya que el tiempo adicional de operación
del vehículo necesario para completar
la entrega dentro del período especificado superó el costo de penalización del envío.
El costo de penalización para shipment[1]
aparece en metrics.costs
y en su índice.
aparece en skippedShipments
.
Restricciones de las ventanas de tiempo flexibles
Como se mencionó brevemente en los Parámetros del modelo de costos, se pueden aplicar los períodos como restricciones flexibles. Las restricciones suaves se diferencian de las restricciones estrictas de la siguiente manera:
- Restricciones estrictas: no se pueden infringir, y el optimizador no ofrece un que infrinja la restricción, incluso si eso significa omitir una envío de datos.
- Restricciones leves: puede infringirse, lo que significa que el optimizador puede proporcionar una solución que infrinja una restricción flexible. Sin embargo, el optimizador también aplica un costo a cualquier incumplimiento. Proporcionas este costo como propiedad adicional en el período, generalmente como un costo por hora para cada hora antes o después del período en el que se produce la actividad.
Los períodos se suavizan con softStartTime
o softEndTime
en lugar de
startTime
o endTime
, respectivamente, y mediante la configuración
costPerHourBeforeSoftStartTime
o costPerHourAfterSoftEndTime
.
Utiliza limitaciones de plazos flexibles cuando deben llevarse a cabo los retiros o las entregas. dentro de un período específico, pero el retiro o la entrega dentro de ese período no se absolutamente necesarios. Puedes usar restricciones de tiempo estrictas y flexibles en conjunto para expresar los objetivos comerciales. Por ejemplo:
- Período de dificultad: Indica el horario de atención de un cliente, p. ej., desde De 9 a.m. a 5 p.m.
- Período reducido: indica el plazo de entrega o retiro que Coincide con la notificación enviada al cliente, por ejemplo, de 9 a.m. a 1 p.m.
En este ejemplo, el envío que se omitió anteriormente porque su tiempo de que la ventana comenzó demasiado tarde se atenuó la restricción de la hora de inicio. El otro los envíos han tenido sus períodos de tiempo los tiempos de finalización también se suavizaron.
Consulta un ejemplo de solicitud con tiempo rígido y flexible ventanas
{ "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 } ] } }
Ver una respuesta a la solicitud de ejemplo con períodos blandes de tiempo
{ "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 } } }
En el ejemplo en el que se omitió por completo el ejemplo con solo las restricciones de período de tiempo estricto
shipment[1]
, lo que reduce el plazo de entrega hace que esté disponible
antes de la hora de inicio de su período. Del mismo modo, suavizar los tiempos de finalización de
otros envíos permitieron que se entregara shipment[2]
después de su período
hasta el final.
Al mismo tiempo, cambiaron los costos y los envíos totales:
totalCost
: bajó de 81.283 a 64.797.- total de envíos completados: aumentó de 2 a 3
El optimizador encontró una solución menos costosa se relajaron en comparación con el ejemplo anterior.
Por último, la propiedad metrics.costs
también incluye una clave nueva para indicar la
costo real incurrido según el producto de la restricción y la duración
tiempo en el que no se cumplió el plazo de entrega. Es decir:
costPerHourBeforeSoftStartTime
de 2.0 y- el tiempo transcurrido entre la entrega real y el inicio del período: 2.83583 horas
Resultado:
model.shipments.deliveries.time_windows.cost_per_hour_before_soft_start_time
5.6716666666666669
Estas métricas te permiten hacer un análisis de costos para ver la compensación entre los
y las sutiles, que puedes usar para ajustar tus restricciones
que se adapten mejor a las reglas de tu negocio. En este caso, el costo total es
menor que shipment[1].penalty_cost
de 20.0. El optimizador identificó
de que es más rentable entregar el envío antes de lo que es
omitir el envío.