← Dark Intelligence Case · Telemetria & ML

Case

Um Porsche de 1988 sob análise de dados

Não queria mais um projeto com dataset de Fórmula 1. Escrevi um decodificador para a telemetria que o Gran Turismo 7 transmite pela rede, gravei 15 voltas com um Porsche 962C e passei a ter um dataset que ninguém tinha antes.

Python UDP · Salsa20 scikit-learn Física de veículos Projeto pessoal
Trial Mountain · 5.087 m · em vermelho, onde 40 dos 55 erros aconteceram
95.440quadros de telemetria
13quadros perdidos em 26 min
0,075 m/serro na validação física
218 km/hpara grudar no teto

O ponto de partida

Gosto de corrida e queria estudar dados disso. O caminho óbvio era baixar um dataset de Fórmula 1, e foi exatamente por isso que não fui por ele: aquele dado já passou pela mão de todo mundo, e o projeto ia acabar sendo uma reprodução de meia dúzia de tutoriais.

Aí lembrei de uma coisa que eu já sabia por outro motivo. O Gran Turismo 7, no PS5, transmite o estado do carro pela rede local em tempo real. Eu já tinha usado um aplicativo que lia esses dados durante as corridas.

Se um app consegue ler, eu consigo escrever o meu. E aí o dataset deixa de ser baixado e passa a ser um experimento que eu desenho.

A diferença é grande na prática. Com dataset pronto, você aceita as variáveis que alguém escolheu. Gerando o seu, você decide o carro, a pista, o clima, o acerto da suspensão, e pode repetir a sessão inteira mudando uma variável por vez. Vira método experimental em vez de arqueologia.

Como se lê a telemetria

O jogo manda um pacote UDP de 368 bytes, 60 vezes por segundo, criptografado com Salsa20 e sem nenhuma documentação oficial. O formato foi mapeado ao longo de anos pela comunidade do GTPlanet por engenharia reversa.

O trabalho aqui é de leitura de bytes. Os quatro bytes na posição 0x4C são a velocidade em metros por segundo. O byte em 0x90 guarda duas marchas comprimidas em quatro bits cada. A partir de 0x158 vem o tipo de superfície embaixo de cada pneu, o que permite saber quando o carro pisou na zebra ou na grama.

Escrevi o decodificador em Python, com detecção automática da constante de criptografia entre as três conhecidas, validando pelo número mágico do pacote. Cada volta é gravada num arquivo Parquet separado, com 113 colunas por quadro.

A validação que decide se todo o resto presta é simples: a velocidade que o jogo reporta tem que bater com a velocidade derivada do vetor de posição. Se essas duas concordam, os offsets estão certos. O erro mediano ficou em 0,075 m/s.

Primeira descoberta: eu era a variável

Os tempos caíam de forma quase linear ao longo da sessão. A leitura fácil seria degradação de pneu ou queima de combustível deixando o carro mais leve.

Era a curva de aprendizado. Eu estava decorando a pista enquanto tentava medir o carro. Ganhei 2,87 segundos entre as cinco primeiras e as cinco últimas voltas, com correlação de −0,82 contra o número da volta.

Isso inviabiliza estudar degradação nesta sessão: aprendizado e desgaste empurram o tempo em direções opostas e ficam impossíveis de separar. Descontando a tendência, sobra um desvio de 0,93 s, que é a variação natural de pilotagem — e esse é o sinal que os modelos podem tentar explicar.

2 4 6 8 10 12 14 volta 104 105 106 107 108 109 110 tempo (s) -0.31 s por volta
Tempos de volta ao longo da sessão. A reta é a tendência de aprendizado. Para medir pneu de verdade seria preciso outra sessão, depois que o ritmo estabilizasse.

O método: a suspensão como balança

Para responder qualquer pergunta sobre aerodinâmica eu precisava da downforce do carro, e ela não vem na telemetria. Mas a suspensão vem.

Quanto mais rápido o carro anda, mais o ar o empurra contra o chão e mais a mola afunda. Entre 90 e 270 km/h ele baixa 4,2 cm. O problema é converter milímetros em newtons, porque a rigidez da mola é desconhecida.

A calibração veio da frenagem. Sob desaceleração conhecida, a carga que passa para o eixo dianteiro é massa vezes desaceleração vezes altura do centro de gravidade dividido pelo entre-eixos. Newton puro. Uma regressão múltipla separa o efeito da velocidade do efeito da frenagem, e o segundo calibra a mola em newtons por metro.

Resultado: mola dianteira de 112,8 kN/m e coeficiente de downforce de 2,27 newtons por metro quadrado por segundo ao quadrado. O entre-eixos usado na conta, 2,76 m, veio do próprio pacote de telemetria.

1000 2000 3000 4000 5000 velocidade ao quadrado (m²/s²) 0 10 20 30 40 mola comprimida (mm)
Compressão da mola contra velocidade ao quadrado. A reta confirma duas hipóteses de uma vez: mola linear e força aerodinâmica proporcional ao quadrado da velocidade. Resíduo máximo de 5,4 mm sobre 41,8 mm de variação total.

O mito do carro que anda no teto

No Museu Porsche, em Stuttgart, um 956 está montado de cabeça para baixo. A placa ao lado diz que, a 321,4 km/h, a downforce seria suficiente para o carro se sustentar num teto. Ninguém nunca testou: o número veio de cálculo.

Com a downforce medida, dava para refazer a conta. Meu número deu 218 km/h para o 962C dentro do jogo, com faixa de incerteza entre 192 e 250 conforme as suposições de altura do centro de gravidade e distribuição de peso.

E apareceu uma armadilha que a conta clássica não mostra. Calculando também o arrasto, por coast-down, descobri que entre 218 e 238 km/h o carro gruda no teto mas não consegue vencer o próprio arrasto. A carga líquida nos pneus é pequena demais para gerar tração. Ele desacelera, cai abaixo de 218 e despenca.

São dois números, não um: 218 para grudar, 238 para se sustentar.

100 150 200 250 300 velocidade (km/h) 0 2 4 6 8 10 12 14 16 18 força (kN) peso do carro 218 gruda 238 se sustenta
Downforce contra velocidade. A diferença para os 321,4 km/h da placa tem explicação: são carros diferentes, a placa usa coeficiente estimado e eu medi a resposta real da suspensão. Vale lembrar que isso mede a física do simulador, não a do carro de 1988.

Onde eu erro, e com quanta antecedência

Catalogar erro quadro a quadro é direto: travamento de roda é escorregamento baixo na dianteira sob frenagem, patinada é escorregamento alto na traseira sob aceleração, e saída de pista vem do canal de superfície. Foram 55 eventos em 15 voltas.

Eles não estão espalhados. Quarenta deles, 73% do total, acontecem em duas curvas só — as duas mais fechadas do traçado, de 27 e 36 metros de raio, ambas seguidas de aceleração. Nas curvas de raio grande, nenhum erro. O problema é geométrico.

Depois treinei um classificador para estimar a chance de errar no próximo segundo, usando apenas o estado do carro e a curvatura da pista à frente, validado deixando uma volta inteira de fora. AUC de 0,91, com precisão média 16 vezes acima da taxa base.

As patinadas aparecem no dado quase um segundo antes de acontecerem. A 200 km/h, isso são 55 metros percorridos com o erro já escrito.

E o contraponto que dá sentido ao resto: rodando o cálculo de aderência disponível, com elipse de atrito e integração para trás a partir do limite de cada curva, nenhum dos 55 erros era fisicamente inevitável. Em todos havia margem de sobra. O carro sempre podia salvar. Falhou a execução, não o equipamento — o que é boa notícia, porque erro de execução se treina.

O trajeto

A ordem importa mais que as ferramentas. Cada etapa só existiu porque a anterior foi validada.

Fazer o pacote chegar

Antes de qualquer análise, provar que o dado atravessa a rede. O firewall do Windows bloqueava a porta de entrada em silêncio, sem erro nenhum. Isso gerou duas ferramentas: um diagnóstico que mostra tudo sem filtro e um PS5 falso que roda na própria máquina, para separar problema de código de problema de rede.

Provar que os bytes estão nos lugares certos

Comparar a velocidade reportada pelo jogo com a velocidade derivada do vetor de posição. Duas fontes independentes para a mesma grandeza. Erro mediano de 0,075 m/s fechou essa questão.

Alinhar as voltas por distância

Duas voltas de tempos diferentes não alinham no eixo do tempo. Reamostrar por distância percorrida é o que torna possível comparar qualquer coisa entre voltas.

Deixar a pista se descrever sozinha

A curvatura da linha de referência segmenta o traçado em curvas e retas sem que ninguém precise marcar nada na mão. Dez curvas detectadas, e a soma dos tempos de setor fecha com o cronômetro do jogo dentro de 0,1 s.

Medir o carro antes de modelar o piloto

Downforce, coeficiente de atrito e arrasto saem por regressão e por coast-down. Com esses três números, várias perguntas viram aritmética em vez de palpite.

Só então treinar modelos

Consumo de combustível, clone comportamental de pilotagem e previsão de erro. Todos validados deixando uma volta inteira de fora, e todos comparados contra uma linha de base burra antes de qualquer comemoração.

O que os erros ensinaram

Foram onze problemas ao longo do projeto. Três eram bugs que produziam resultado plausível, sem lançar exceção e sem baixar métrica. Nenhum deles daria sinal sozinho.

float32

Um coeficiente zerado por cancelamento numérico

Na regressão da suspensão, o termo da velocidade é da ordem de 1e-6 e o da frenagem de 1e-4. Em precisão simples eles se cancelam e o segundo coeficiente sai zerado. O R² continuava alto, então nada acusava. Só apareceu quando comparei o resultado com uma medição direta da compressão sob frenagem, que dava 8,7 mm onde o modelo dizia zero.

LiçãoPrecisão numérica não é detalhe de implementação quando as escalas das variáveis diferem em ordens de grandeza. E métrica alta não é evidência de que a conta está certa.
vazamento

Um R² de 0,98 que era tautologia

O clone comportamental previa meus pedais com precisão suspeita. Eu tinha incluído aceleração longitudinal entre as variáveis de entrada — e aceleração longitudinal negativa é, literalmente, frear. O modelo não estava aprendendo nada, estava lendo a resposta. Removidas as variáveis que são consequência do pedal, o número honesto ficou em 0,76.

LiçãoToda variável precisa passar por um teste causal antes de entrar: ela é anterior ao alvo, ou é efeito dele? Resultado bom demais é motivo para desconfiar, não para publicar.
525 km/h

O cálculo certo respondendo à pergunta errada

A primeira tentativa de medir downforce usava o g lateral máximo em função da velocidade. Deu 525 km/h para a velocidade de teto, um absurdo. A matemática estava correta. O método é que estava errado: em Trial Mountain não existe curva rápida o bastante para chegar ao limite de aderência a 240 km/h, então aquilo media a geometria da pista, não o carro.

LiçãoAntes de confiar num número, perguntar de onde vem a variação que o produz. Se o dado nunca visitou a região que importa, nenhum estimador conserta isso.
cobertura

Setores que não cobriam a volta inteira

Trechos curtos entre curvas estavam sendo descartados na segmentação, então a soma dos tempos de setor não fechava com o cronômetro. O erro era de 1%: pequeno o bastante para passar despercebido, grande o bastante para envenenar toda a análise de onde se perde tempo.

LiçãoQuando existe uma identidade que precisa fechar — a soma das partes contra o todo — transforme isso em asserção no código. Foi o que passou a existir depois.
limiar

Um detector que chamava frenagem normal de erro

O primeiro critério de travamento de roda marcava 28% de todos os quadros de frenagem como falha. Sob freio forte o pneu sempre escorrega um pouco: a mediana é 0,93. Isso não é travar, é frear. Ajustado o limiar, sobraram nove travamentos reais.

LiçãoLimiar escolhido no chute vira definição silenciosa do fenômeno. Olhar a distribuição antes de fixar o corte custa cinco minutos e evita conclusões inteiras erradas.
confusão

Combustível aparecendo como causa de lentidão

Num dos modelos, o nível de combustível surgiu como a variável mais importante para explicar o tempo de volta, com correlação de +0,82. Fisicamente faria sentido, já que carro mais pesado é mais lento. Mas o combustível cai monotonicamente com o número da volta, e os tempos caíam por aprendizado. O modelo capturou minha curva de aprendizado disfarçada de peso.

LiçãoQuando duas variáveis evoluem juntas com o tempo, a correlação entre elas não diz nada. Delineamento experimental resolve isso antes da modelagem; nenhum algoritmo resolve depois.

A lição que ficou maior que o projeto

O modelo de tempo de volta, que era o objetivo original, empatou exatamente com uma linha de base que só chutava a média do setor. Quinze voltas são quinze amostras, e nenhum modelo se sustenta com isso.

O modelo de consumo de combustível, treinado nos mesmos dados, chegou a R² de 0,998 com erro de 0,4% no consumo por volta.

A diferença não está no algoritmo. Um trata a volta como amostra, o outro trata o segundo. A mesma sessão tem 15 ou 95 mil linhas, dependendo da pergunta.

Quando o primeiro modelo falhou, a leitura natural teria sido “faltam dados”. Faltava reformular o problema. Escolher a unidade de análise é uma decisão de projeto, e ela determina o que é possível descobrir antes de qualquer escolha técnica.

O que ficou de pé

Captura via UDP com decodificação da criptografia, reamostragem por distância, detecção automática de curvas, medição de parâmetros físicos do veículo, clone comportamental de pilotagem e previsão de erro. Tudo em cima de um dataset que não existia antes.

PythonNumPypandasParquet scikit-learnLightGBMMatplotlib Salsa20UDP sockets