Demandas e limites de carga

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

Como mencionado em Restrições da janela de tempo de retirada e entrega, a mensagem OptimizeToursRequest (REST, gRPC) contém diversas 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 quantidade de carga que uma determinada envio consome. Consulte a documentação da mensagem Shipment (REST, gRPC).

Juntas, essas duas restrições permitem que o otimizador atribua adequadamente remessas a veículos de uma maneira que corresponda melhor à capacidade da frota e às demandas de remessa.

Este documento aborda loadLimits e loadDemands em detalhes.

Demandas e limites 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 carregamento, como nos exemplos a seguir:

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

Neste guia, usamos weightKg como um tipo de 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 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 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 consome o loadLimits do veículo atribuído somente se os dois tiverem chaves de tipo de carga correspondentes. Por exemplo, uma remessa com loadDemands de:

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

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

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

pode conseguir 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 implicitamente uma capacidade weightKg ilimitada devido à ausência de um limite de carga weightKg, de modo que o veículo não é limitado pela demanda de peso da remessa.

Transferência de carga entre remessas e veículos

À medida que as remessas são coletadas e entregues por veículos, o loadDemand é transferido entre ela e o veículo. É possível conferir os carregamentos do veículo na entrada de mensagens OptimizeToursResponse (REST, gRPC)routes.transitions de um determinado veículo. A sequência é a seguinte:

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

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

Um exemplo completo com demandas e limites de carga

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

{
  "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.
  • 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.

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

{
  "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 carregamento adicionadas afetam a ordem de visits:

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

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

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

Cada entrada de transitions inclui a carga total de veículos 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étrica routes[0].metrics e metrics.aggregatedRouteMetrics incluem uma propriedade maxLoads. O valor do tipo weightKg é 80, que representa a parte do trajeto do veículo que transportou shipments[2] para o local de entrega.

Restrições de limite de carga flexível

Assim como nos períodos descritos em Restrições das janelas 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 de maxLoad no tipo especificado. As propriedades softMaxLoad e costPerUnitAboveSoftMax expressam uma restrição flexível, e cada unidade que exceder softMaxLoad incorre em um custo de costPerUnitAboveSoftMax.

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

  • equilibrar remessas com mais veículos do que o número mínimo necessário quando for econômico
  • Expressar a preferência do motorista pelo número de itens que podem retirar e entregar confortavelmente em um determinado trajeto.
  • carregando veículos abaixo da capacidade física máxima para limitar o desgaste e reduzir os custos de manutenção

As restrições de limites de carregamentos rígidos e flexíveis podem ser usadas juntas. Por exemplo, um limite de carga rígida pode expressar o peso máximo de carga que um veículo pode transportar com segurança ou o número máximo de itens que cabem no veículo por vez, enquanto um limite de carga flexível pode ser o peso ou o número máximo de itens que exigiriam a capacidade do motorista de colocar tudo no veículo.