Cruise control preso: como modelar state machine de desligamento seguro

Cruise control preso: como modelar state machine de desligamento seguro

Cruise control “preso” parece roteiro de filme, mas já rolou de verdade — e o detalhe que quase todo mundo ignora é que, nesses casos, o problema quase nunca é “o carro ficou doido”. Normalmente é uma combinação de estado do sistema (modo de condução), interação humana (botões/avisos), e limitações mecânicas/eletrônicas. Segundo o Sapo.pt, em outubro de 2004, na França, um condutor relatou incapacidade de desligar o cruise control no Renault Vel Satis, com o carro a cerca de 200 km/h por ~150 km. Esse tipo de episódio empurra a engenharia: você não quer só “manter velocidade”, você quer falhar com segurança.

Cruise control preso: o que “parece impossível” na prática

Quando alguém diz “não consigo desligar”, existem algumas leituras técnicas possíveis. O cruise control é um sistema de malha fechada: ele compara velocidade alvo vs. velocidade real e atua (aceleração/freio/borboleta/atuações de controle eletrônico). Para desligar, o software precisa reconhecer um comando de saída (ex.: botão OFF, apertar freio, mudança de posição de marcha, limite de velocidade, detecção de condições de segurança).

Então, quando o condutor afirma que não desligou, ou:

  • o comando de desligar não foi reconhecido (interface/contato/botão/registro do evento),
  • o sistema entrou num estado em que “não aceita desligamento” sem uma condição adicional (ex.: modo específico, reativação automática),
  • houve falha intermitente em sensores de velocidade/atuadores, levando o controle a “continuar” mesmo após tentativa de OFF,
  • ou o que parecia cruise preso era, na verdade, uma lógica diferente (ex.: controle de velocidade adaptativo/assistências) com semântica parecida para o usuário.

O ponto importante: mesmo quando investigações não encontram falha técnica definitiva (o que o Sapo.pt menciona em vários casos), esses incidentes servem como sinal de que a UX de segurança e a robustez de estados precisam ser testadas como se fossem adversários — porque, na estrada real, elas são.

O caso francês (2004): por que ele virou “história”

Segundo o Sapo.pt, Hicham Draa, com um Renault Vel Satis na A71 (entre Clermont-Ferrand e Vierzon), relatou que o cruise control ficou bloqueado. Ele teria tentado reduzir a velocidade sem sucesso, e o veículo seguiu numa operação de escolta e gestão de tráfego com mobilização de gendarmes, bombeiros e equipes para minimizar riscos aos demais motoristas.

O que me chama atenção nesse tipo de narrativa é o “gap” entre expectativa humana e comportamento do sistema. Motoristas esperam que OFF seja absoluto e imediato. Já o sistema automotivo frequentemente tem camadas:

  • camada de entrada (botões, pedal do freio, alavancas),
  • camada de decisão (state machine: armado, ativo, desativando, degradado),
  • camada de atuadores (o quanto o controle pode atuar sem violar limites),
  • camada de supervisão (falhas, redundância, “fail-operational” vs “fail-safe”).

Se um comando chega mas a máquina de estados não transita para o estado correto (ou transita mas reativa por alguma heurística), o condutor percebe como “não desliga”. E, num cenário a ~200 km/h, qualquer atraso de alguns segundos vira um problema operacional.

Depois de 2004: como a engenharia respondeu (sem glamour, mas com impacto)

Uma lição frequente desses episódios é que “funcionou em bancada” não basta. O que você quer é:

  • tratamento consistente de comandos de desligamento (inclusive quando múltiplos eventos ocorrem em sequência),
  • redundância (se um sensor ou uma interface falha, outra via precisa dominar),
  • supervisão de reativação (nenhuma lógica deve reativar controle quando o usuário explicitamente tentou desativar),
  • tratamento de falhas degradadas (por exemplo: se o sistema detecta inconsistência, ele deve reduzir atuação e solicitar ação clara ao motorista).

No mundo real, ADAS e sistemas de assistência entram na conversa. Hoje, dependendo do carro, o “cruise control” que o motorista chama de cruise pode, na verdade, ser controle de velocidade adaptativo, assistente de faixa, ou alguma variação que compartilha hardware/software. Isso aumenta o risco de que o usuário não saiba qual botão/gesto é o “kill switch” correto.

Comparação técnica: cruise simples vs. controle adaptativo vs. “assistências combinadas”

Para desenvolvedores, a analogia natural é: cada sistema é uma componente num grafo de estados. Quanto mais sofisticado, mais fácil é errar transições e guardas (conditions). Em termos práticos:

  • Cruise control clássico: alvo de velocidade e ação contínua. Desativar tende a ser direto (OFF e freio).
  • Controle adaptativo: além de manter velocidade, monitora distância ao veículo à frente. Em certos cenários, pode “parecer” que não desliga se o sistema entender que ainda está sob outra condição de controle.
  • Pacotes com ADAS: o estado do veículo pode influenciar o controle (ex.: seleção de modo de condução, ativações e desativações automáticas, integração com ABS/ESC). Se a interface do usuário não estiver modelada corretamente, a sensação de “travou” aparece.

O Sapo.pt aponta que, na maioria dos casos investigados, não se comprovou falha técnica. Ainda assim, o comportamento percebido pelo condutor foi suficiente para acelerar evolução. Para mim, isso é um lembrete de engenharia: “nenhuma evidência de falha” não significa “nenhum problema”. Significa que a falha pode ser intermitente, de difícil reprodução, ou estar no limite entre UX e controle.

Na Prática: como os sistemas deveriam garantir “desativação confiável” (com um state machine simples)

Vou traduzir isso para um padrão que eu aplico em sistemas críticos: state machine com guardas explícitas e prioridade alta para “comandos de desligar”. Não é automotivo, mas é a ideia.

  1. Defina estados: INACTIVE, ACTIVE, DEACTIVATING, FAULT_DEGRADED.
  2. Crie uma lista de eventos de saída com prioridade máxima: OFF_BUTTON, BRAKE_PEDAL, GEAR_CHANGE, MANUAL_OVERRIDE.
  3. Em qualquer estado, se ocorrer evento de saída, transicione para DEACTIVATING e assegure que não há caminho para reativar enquanto o comando de desligar estiver “recentemente registrado”.
  4. Separe “controle de aceleração” do “controle de segurança”: o primeiro depende da malha; o segundo deve dominar em falha.
const State = {
  INACTIVE: "INACTIVE",
  ACTIVE: "ACTIVE",
  DEACTIVATING: "DEACTIVATING",
  FAULT_DEGRADED: "FAULT_DEGRADED",
};

const Event = {
  OFF_BUTTON: "OFF_BUTTON",
  BRAKE_PEDAL: "BRAKE_PEDAL",
  GEAR_CHANGE: "GEAR_CHANGE",
  MANUAL_OVERRIDE: "MANUAL_OVERRIDE",
  TICK: "TICK",
  SENSOR_INCONSISTENT: "SENSOR_INCONSISTENT",
};

function nextState(state, event, ctx) {
  // Guard: eventos de desligamento sempre vencem
  const killSwitch =
    event === Event.OFF_BUTTON ||
    event === Event.BRAKE_PEDAL ||
    event === Event.GEAR_CHANGE ||
    event === Event.MANUAL_OVERRIDE;

  if (killSwitch) {
    ctx.lastKillSwitchAt = Date.now();
    return State.DEACTIVATING;
  }

  // Se sensores estão inconsistentes, desça para modo seguro
  if (event === Event.SENSOR_INCONSISTENT) {
    return State.FAULT_DEGRADED;
  }

  switch (state) {
    case State.INACTIVE:
      if (event === Event.TICK && ctx.canEngage() && !ctx.isInKillGraceWindow()) {
        return State.ACTIVE;
      }
      return state;

    case State.ACTIVE:
      // Durante DEACTIVATING não aceitamos reengage automático
      return state;

    case State.DEACTIVATING:
      if (event === Event.TICK) {
        // após estabilizar atuadores, vai para inativo
        ctx.releaseActuatorsToSafe();
        return State.INACTIVE;
      }
      return state;

    case State.FAULT_DEGRADED:
      if (event === Event.TICK) {
        // permanece degradado até reconfigurar
        ctx.requestDriverIntervention();
      }
      return state;

    default:
      return state;
  }
}

Por que isso rankearia como “correto”: porque elimina a ambiguidade do tipo “apertei OFF mas o sistema reativou”. A grace window e a prioridade do kill switch evitam reingresso automático mesmo que algum módulo tente rearmar.

Erros Comuns: o que devs e equipes costumam fazer (e depois sofrem)

  • Tratar “OFF” como mais um evento na fila, sem prioridade. Resultado: outro evento processado depois “ganha” e reativa.
  • Não modelar a máquina de estados. Sem state machine, o comportamento vira “efeito colateral de condições” e a reprodução fica impossível.
  • Perder telemetria exatamente nos momentos críticos. O bug pode ser intermitente; se você não registra transições, vira caso “não comprovado”.
  • Não alinhar UX com controle: o usuário precisa saber o “kill switch” real (freio? botão? combinação?). Em automóveis, a semântica importa.
  • Assumir causalidade única: “se OFF foi apertado, então o sistema desligou”. Na prática, pode haver conflito de módulos (ADAS integrado) e o efeito percebido divergir.
  • Testar só cenários felizes. Você precisa de testes com sequência de eventos estranha: OFF + alteração de marcha + variação de sensores + oscilação intermitente.

Implicações práticas para quem programa (e não só para carros)

Essa história do cruise control preso não é sobre automóvel em si. É sobre segurança operacional.

Quando você constrói qualquer sistema com automação que pode “agir sem você”, você precisa responder a perguntas que motoristas fazem sem formalidade:

  • Se eu apertar “desligar”, isso é garantido?
  • Em quanto tempo eu deixo de atuar?
  • Existe algum caminho que reative automaticamente depois do desligamento?
  • Se um sensor falhar, o sistema ainda respeita o “kill switch”?
  • Como eu registro e expando evidências quando algo “não deveria acontecer”?

Em sistemas web/AI, isso aparece em rotinas como: automação de ações, bots, agentes com ferramentas, pipelines que executam tarefas em background. Se o “stop” não domina, você cria a versão digital do cruise preso.

FAQ

Se o cruise control “não desligou”, isso prova defeito técnico?

Não necessariamente. Segundo o Sapo.pt, em vários episódios não foi comprovada falha técnica. Pode ser falha intermitente, integração com outros modos, ou um estado do sistema que não respondeu ao evento de desligamento como o condutor esperava.

A investigação costuma falhar por falta de evidência?

Sim. Em casos intermitentes, logs e telemetria podem não capturar o suficiente. Além disso, o comportamento percebido depende da sequência de eventos na estrada, que é difícil de reproduzir.

Que “kill switch” deveria existir em sistemas de controle automático?

Em engenharia, o ideal é que exista um comando de desligamento com prioridade máxima e que o estado final não permita reativação automática por um período de proteção.

Controle adaptativo é mais propenso a confundir o motorista?

Pode ser. Como o comportamento depende de múltiplas variáveis (distância ao veículo à frente, limites e condições do trajeto), a desativação pode parecer “demorada” ou “ineficaz” se a UX não deixar claro o que interrompe o sistema de fato.

Como isso se conecta com desenvolvimento de software e IA?

Em agentes e automações, “parar” precisa ser determinístico e dominante. Se um agente continua executando após um comando de stop, você está criando um “cruise preso” em software.

Gostou? Me segue no GitHub e deixa um comentário se tiver dúvida ou quiser aprofundar algum ponto.

Y

Yuri Sousa

Front-End Developer / Designer

Desenvolvedor apaixonado por criar experiências digitais acessíveis e visualmente perfeitas. Escrevo sobre desenvolvimento web, design e tecnologia.