Demandas e limites de carga

Este guia descreve loadDemands e loadLimits e como eles se relacionam.

Conforme mencionado em Restrições de janela de retirada e entrega, a mensagem OptimizeToursRequest (REST, gRPC) contém várias propriedades que especificam restrições no problema que está sendo otimizado. Várias propriedades OptimizeToursRequest representam restrições de carga.

Veículos e remessas têm propriedades físicas que precisam ser consideradas ao planejar uma rota.

  • Veículos: a propriedade loadLimits especifica a carga máxima que o veículo pode processar. Consulte a documentação da mensagem Vehicle (REST, gRPC).
  • Envios: a propriedade loadDemands especifica a carga que um determinado envio consome. Consulte a documentação da mensagem Shipment (REST, gRPC).

Juntas, essas duas restrições permitem que o otimizador atribua os envios aos veículos de maneira adequada, de acordo com a capacidade da frota e as demandas de envio.

O restante deste documento discute loadLimits e loadDemands em detalhes.

Limites e demandas de carga: tipos

Você expressa cada demanda de carga e restrição de limite em termos de um tipo.

Você pode fornecer seu próprio conjunto de tipos de carga, como nos exemplos a seguir:

  • peso
  • volume
  • medidas lineares
  • nomes de itens ou equipamentos que estão sendo transportados

Este guia usa weightKg como exemplo.

Shipment.loadDemands e Vehicle.loadLimits usam o tipo map de buffers de protocolo, com chaves string que representam os tipos de carga.

Os valores de Shipment.loadDemands usam a mensagem Load (REST, gRPC). A mensagem Load tem uma única propriedade amount que representa a capacidade necessária para concluir o envio no tipo especificado.

Os valores de Vehicle.loadLimits usam a mensagem LoadLimit (REST, gRPC). A mensagem LoadLimit tem várias propriedades, com maxLoad representando a capacidade máxima de carga do veículo no tipo especificado.

O loadDemands de uma remessa só vai consumir o loadLimits do veículo atribuído se os dois tiverem chaves de tipo de carga correspondentes. Por exemplo, um envio com loadDemands de:

"loadDemands": {
  "weightKg": {
    "amount": 50
  }
}

requer 50 unidades de carga no tipo weightKg para que o envio seja concluído. Um veículo com loadLimits de:

"loadLimits": {
  "weightKg": {
    "maxLoad": 100
  }
}

pode concluir o envio, já que o maxLoad do veículo no tipo weightKg é maior ou igual ao loadDemands do envio no tipo weightKg. No entanto, um veículo com loadLimits de:

"loadLimits": {
  "equipmentRackStorage": {
    "maxLoad": 10
  }
}

tem capacidade ilimitada de weightKg devido à ausência de um limite de carga de weightKg. Assim, o veículo não é limitado pela demanda de peso do envio.

Transferência de carga entre remessas e veículos

À medida que os envios são retirados e entregues por veículos, o loadDemand do envio é transferido entre o envio e o veículo. É possível conferir as cargas do veículo na entrada routes.transitions da mensagem OptimizeToursResponse (REST, gRPC) para um determinado veículo. A sequência é a seguinte:

  1. A capacidade de carga necessária é definida para o envio como uma loadDemand.
  2. A remessa é retirada pelo veículo atribuído, e o vehicleLoads do veículo aumenta de acordo com o valor de loadDemand. Essa transferência é representada por visits.loadDemands positivo na mensagem de resposta.
  3. O veículo entrega a remessa, e o vehicleLoads do veículo diminui em uma quantidade igual ao loadDemand da remessa entregue. Essa transferência é representada por visits.loadDemands negativo na mensagem de resposta.

O vehicleLoads de um veículo não pode exceder o loadLimits especificado em nenhum ponto da rota.

Um exemplo completo com limites e demandas de carga

Veja um exemplo de solicitação com demandas de carga e limites

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

O exemplo de solicitação contém vários parâmetros relacionados à carga:

  • shipments[0] tem uma demanda de carga de 50 weightKg.
  • O shipments[1] tem uma demanda de carga de 10 weightKg.
  • shipments[2] tem uma demanda de carga de 80 weightKg.
  • vehicles[0] tem um limite de carregamento de 100 weightKg.

Ver uma resposta à solicitação com demandas de carga e limites

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

As restrições de carga adicionadas afetam a ordem de visits:

  1. shipment[0] é retirado
  2. shipment[1] foi retirado
  3. shipment[0] é entregue
  4. shipment[1] é entregue
  5. shipment[2] é retirado
  6. shipment[2] é entregue

Esse pedido reflete que três remessas não podem ser concluídas pelo veículo ao mesmo tempo porque o total de loadDemands delas excede a loadLimits do veículo.

Cada entrada visits inclui a mudança na carga do veículo resultante da conclusão da Visit. Valores positivos de carga representam o carregamento do envio, e valores negativos representam o descarregamento do envio.

Cada entrada transitions inclui a carga total do veículo durante o Transition. transitions[2], por exemplo, tem uma carga weightKg de 60, representando as cargas combinadas de shipment[0] e shipment[1].

Os objetos de métricas routes[0].metrics e metrics.aggregatedRouteMetrics incluem uma propriedade maxLoads. O valor do tipo weightKg é 80, representando a parte da rota do veículo que transportou shipments[2] até o local de entrega.

Restrições de limite de carga flexível

Assim como os períodos descritos em Restrições da janela de tempo de retirada e entrega, as restrições de limite de carga têm variantes rígidas e flexíveis. A propriedade maxLoad da mensagem LoadLimit expressa uma restrição rígida: o veículo nunca pode transportar carga que exceda o valor maxLoad no tipo especificado. As propriedades softMaxLoad e costPerUnitAboveSoftMax expressam uma restrição flexível, com cada unidade que excede softMaxLoad incorrendo em um custo costPerUnitAboveSoftMax.

As restrições de limite de carga flexível têm vários usos, como:

  • balancear os envios em mais veículos do que o número mínimo necessário quando for mais econômico fazer isso
  • expressar a preferência do motorista pelo número de itens que ele pode coletar e entregar confortavelmente em um determinado trajeto
  • carregar veículos abaixo da capacidade física máxima para limitar o desgaste e reduzir os custos de manutenção

As restrições de limite de carga rígida e flexível podem ser usadas juntas. Por exemplo, um limite de carga fixo pode expressar o peso máximo que um veículo pode transportar com segurança ou o número máximo de itens que um veículo pode transportar ao mesmo tempo, enquanto um limite de carga flexível pode ser o peso ou número máximo de itens que sobrecarregam a capacidade do motorista de colocar tudo no veículo.