В этом руководстве описываются loadDemands
и loadLimits
, а также их взаимосвязь.
Как упоминалось в разделе «Ограничения времени забора и доставки» , сообщение OptimizeToursRequest
( REST , gRPC ) содержит ряд свойств, которые определяют ограничения для оптимизируемой задачи. Несколько свойств OptimizeToursRequest
представляют собой ограничения нагрузки .
Транспортные средства и грузы имеют физические свойства, которые необходимо учитывать при планировании маршрута.
- Транспортные средства : свойство
loadLimits
определяет максимальную нагрузку, которую может выдержать транспортное средство. См. документацию по сообщениямVehicle
( REST , gRPC ). - Shipments : свойство
loadDemands
определяет объём загрузки, потребляемый конкретной отправкой. См. документацию по сообщениямShipment
( REST , gRPC ).
В совокупности эти два ограничения позволяют оптимизатору правильно распределять грузы по транспортным средствам таким образом, чтобы они наилучшим образом соответствовали вместимости вашего автопарка и потребностям в грузоперевозках.
В оставшейся части документа подробно обсуждаются loadLimits
и loadDemands
.
Требования и ограничения нагрузки: типы
Каждое требование по нагрузке и ограничение предела выражается в терминах типа .
Вы можете предоставить свой собственный набор типов нагрузки, как в следующих примерах:
- масса
- объем
- линейные измерения
- названия перевозимых предметов или оборудования
В этом руководстве в качестве примера типа используется weightKg
.
Как Shipment.loadDemands
, так и Vehicle.loadLimits
используют тип map
Protocol Buffers со string
ключами, которые представляют типы нагрузки.
Значения Shipment.loadDemands
используют сообщение Load
( REST , gRPC ). Сообщение Load
имеет единственное свойство amount
, которое указывает объём груза, необходимый для завершения отгрузки указанного типа.
Значения Vehicle.loadLimits
используют сообщение LoadLimit
( REST , gRPC ). Сообщение LoadLimit
имеет несколько свойств, среди которых maxLoad
представляет максимальную грузоподъёмность транспортного средства указанного типа.
Значение loadDemands
для груза расходует loadLimits
назначенного транспортного средства только в том случае, если у обоих транспортных средств совпадают ключи типа груза. Например, для груза с loadDemands
:
"loadDemands": {
"weightKg": {
"amount": 50
}
}
Для завершения перевозки требуется 50 единиц груза с типом weightKg
. Транспортное средство с loadLimits
:
"loadLimits": {
"weightKg": {
"maxLoad": 100
}
}
Возможно , нам удастся завершить перевозку, поскольку maxLoad
транспортного средства в типе weightKg
больше или равна loadDemands
в типе weightKg
. Однако транспортное средство с loadLimits
:
"loadLimits": {
"equipmentRackStorage": {
"maxLoad": 10
}
}
неявно имеет неограниченную грузоподъемность weightKg
из-за отсутствия ограничения по весу weightKg
, поэтому транспортное средство не ограничено требуемым весом груза.
Перераспределение груза между партиями и транспортными средствами
По мере того, как грузы забираются и доставляются транспортными средствами, loadDemand
груза переносится между грузом и транспортным средством. Загрузки транспортного средства можно увидеть в записи routes.transitions
сообщения OptimizeToursResponse
( REST , gRPC ) для данного транспортного средства. Последовательность выглядит следующим образом:
- Требуемая грузоподъемность для отправки определяется как
loadDemand
. - Груз забирается назначенным транспортным средством, и
vehicleLoads
транспортного средства увеличивается на величинуloadDemand
груза. Этот перенос отображается положительным значениемvisits.loadDemands
в ответном сообщении. - Транспортное средство доставляет груз, и его
vehicleLoads
уменьшается на величинуloadDemand
доставленного груза. Этот перенос отображается отрицательным значениемvisits.loadDemands
в ответном сообщении.
vehicleLoads
транспортного средства не может превышать указанные loadLimits
ни в одной точке маршрута.
Полный пример с требованиями к нагрузке и ограничениями
Посмотрите пример запроса с требованиями к нагрузке и ограничениями
{ "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 } } } ] } }
Пример запроса содержит несколько параметров, связанных с нагрузкой:
-
shipments[0]
имеет потребность в загрузке 50weightKg
. -
shipments[1]
имеют требуемую загрузку 10 кгweightKg
. -
shipments[2]
имеют требуемую массу груза 80weightKg
. -
vehicles[0]
предельная нагрузка составляет 100weightKg
.
Посмотрите ответ на запрос с требованиями и ограничениями по нагрузке.
{ "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 } } }
Добавленные ограничения нагрузки влияют на порядок visits
:
-
shipment[0]
забран -
shipment[1]
забран -
shipment[0]
доставлен -
shipment[1]
доставлен -
shipment[2]
забирается -
shipment[2]
доставлен
Этот заказ отражает тот факт, что три поставки не могут быть выполнены транспортным средством одновременно, поскольку их общая loadDemands
превышает loadLimits
транспортного средства.
Каждая запись visits
включает изменение загрузки транспортного средства в результате завершения Visit
. Положительные значения загрузки соответствуют загрузке, отрицательные — разгрузке.
Каждая запись transitions
включает общую загрузку транспортного средства во время Transition
. Например, transitions[2]
имеет нагрузку weightKg
равную 60, что представляет собой объединенную загрузку shipment[0]
и shipment[1]
.
Объекты метрик routes[0].metrics
и metrics.aggregatedRouteMetrics
включают свойство maxLoads
. Значение типа weightKg
равно 80, что соответствует части маршрута транспортного средства, доставившего shipments[2]
до места назначения.
Мягкие ограничения предела нагрузки
Как и временные окна, описанные в разделе «Ограничения временных окон забора и доставки» , ограничения по нагрузке бывают жёсткими и мягкими. Свойство maxLoad
сообщения LoadLimit
выражает жёсткое ограничение: транспортное средство ни при каких обстоятельствах не должно перевозить груз, превышающий значение maxLoad
для указанного типа. Свойства softMaxLoad
и costPerUnitAboveSoftMax
выражают мягкое ограничение: каждая единица груза, превышающая softMaxLoad
, влечёт за собой стоимость costPerUnitAboveSoftMax
.
Ограничения по мягкой нагрузке имеют несколько применений, например:
- балансировка поставок между большим количеством транспортных средств, чем минимально необходимое количество, когда это экономически эффективно
- выражая предпочтения водителя относительно количества товаров, которые он может с комфортом забрать и доставить по заданному маршруту
- загрузка транспортных средств ниже их максимальной физической грузоподъемности для ограничения износа и снижения затрат на техническое обслуживание
Жёсткие и мягкие ограничения по нагрузке могут использоваться совместно. Например, жёсткий предел нагрузки может выражать максимальный вес груза, который транспортное средство может безопасно перевозить, или максимальное количество предметов, которые могут поместиться в транспортное средство одновременно, в то время как мягкий предел нагрузки может представлять собой максимальный вес или количество предметов, которые могут оказаться слишком сложными для водителя, чтобы разместить всё в транспортном средстве.