Sobre o projeto

O b166er é o robô móvel de manipulação no centro da minha pesquisa de doutorado: uma base móvel Pioneer 3-AT combinada com um braço robótico Mitsubishi RV-M2, integrados sob um único mestre ROS Noetic. Cada parte chegou com seu próprio controlador, pensado para operar isolado — a integração é o trabalho de fazer os dois conversarem em tempo real, no mesmo robô.
A arquitetura usa duas máquinas. A Shiroi é o notebook de desenvolvimento, de onde saem a programação e a visualização em RViz. A NUC (Intel NUC 5i5RYH, identificada na rede como b166er-nuc.local) é o computador embarcado no robô: hospeda o mestre ROS e fala diretamente com o hardware — a base Pioneer por USB-serial, o braço Mitsubishi via Arduino com rosserial, e uma IMU Sparton AHRS-8, também por USB-serial. O ROS Noetic em ambas as máquinas é fornecido pelo RoboStack, não por uma instalação nativa de Ubuntu — uma escolha que evita travar o projeto numa única versão de sistema operacional.
Lições aprendidas
Nem toda lição aprendida nessa integração é sobre o robô em si. Boa parte é sobre o ambiente de desenvolvimento que sustenta o projeto — e quando uma dessas lições cresce o suficiente para virar um tutorial completo, ela ganha uma página técnica própria, linkada a partir daqui:
- RoboStack: instale o ROS1 sem Docker, em qualquer sistema — como o b166er usa RoboStack para rodar ROS1 Noetic via Conda, em Linux e fora dele, sem Docker e sem dual-boot.
- ZeroTier: acesse o robô de qualquer lugar sem VPN dedicada — como conectar o notebook de desenvolvimento ao NUC embarcado via ROS, independente de estar no laboratório.
06 Nov 2026
O Alarme que Confundia a Chave Abrindo com o Olhal Escapando
Desde outubro, a libera tinha um alarme: se a ponta subisse oito milímetros em menos de um terço de segundo, era sinal de que o arame tinha escapado por cima do degrau, e a missão tentava reassentar. Classifiquei pela sonda todos os alarmes das baterias. Metade não era perda nenhuma: era a chave abrindo. Quando o gatilho solta, a mola devolve o olhal para cima, ele encosta na face de cima do degrau e leva a ponta junto, exatamente como o orientador tinha descrito. O alarme interrompia a abertura no melhor momento, e o reassentamento que vinha depois quase nunca salvava a missão.
O tamanho da subida no instante do alarme não separa os dois casos. O que separa é o que vem depois: na chave abrindo, a subida para em poucos milímetros; quando o olhal escapa, ela continua. O alarme passou a esperar meio segundo, com a libera seguindo, e só conta como perda se a subida passar de catorze milímetros.
Na bateria, o caminho novo não foi testado: houve só dois alarmes, e os dois subiram além do limite. O total foi a dezenove, mas sem nenhum alarme liberado essa diferença não vem da mudança. Ficou como rascunho, à espera de uma bateria dirigida ao viés em que os alarmes aparecem.
05 Nov 2026 (tarde)
O Laser que Procura a Parede Sozinho
As missões que perdiam a tag na aproximação tinham um começo em comum. Na primeira medida, a dois metros, a tag dava o ângulo da parede vinte e cinco a trinta graus errado, sempre para o mesmo lado. O laser existe para corrigir isso, mas procurava a parede só numa faixa estreita em torno do plano que a tag indicava. Com o ângulo tão errado, quase nenhum ponto caía na faixa, a correção era recusada e o robô se aproximava virado para o lado errado, até a tag sair do quadro. Na maioria das vezes, um giro de recuperação a reencontrava. Em algumas, a missão abortava.
Agora, quando a faixa vem quase vazia, o laser testa ângulos até trinta e cinco graus para cada lado e fica com a reta que reúne mais pontos perto da tag. Na bateria, isso aconteceu em quinze das vinte e cinco missões, e nenhuma perdeu a tag na aproximação, contra seis a oito por bateria antes. O total ficou em dezessete, abaixo das vinte e duas da última bateria da versão principal. As oito falhas, porém, vieram todas depois da aproximação e foram tão frequentes nas missões em que a busca disparou quanto nas outras. A decisão de mergear é do orientador.
05 Nov 2026
A Mola Escondida no Firmware Simulado
O salto do ombro tinha uma explicação elegante. O firmware que simula o braço sem encoder transforma a velocidade pedida num ângulo de comando, e deixa esse ângulo correr alguns graus à frente do real quando a junta trava. É uma folga criada para vencer o peso do braço nas juntas que carregam carga. O ombro gira em torno do eixo vertical e não carrega peso, mas tinha quatro graus dessa folga. Com a haste presa no arame, o comando corria quatro graus à frente, e quando o contato escorregava a junta disparava de uma vez: quatro a cinco graus, o tamanho exato dos saltos medidos. Uma mola escondida na simulação, que o motor real não tem.
Limitei a folga do ombro ao atraso natural do controlador, e a bateria desmentiu a parte que mais importava. Os saltos não diminuíram, e apareceu uma falha nova no fim da missão, ao tirar o dedo do anel depois de abrir a chave. Nessa simulação, a força do ombro vem justamente da folga. Sem ela, faltou força para vencer o atrito. O total caiu de vinte e dois para quinze, a mudança ficou como rascunho e a simulação voltou ao firmware anterior.
A lição é que a emulação mistura força e posição numa coisa só. Separar as duas, com o esforço limitado como o de um motor travado, mexe no núcleo do modo sem encoder e é uma decisão para o orientador.
04 Nov 2026 (noite)
A Hipótese Incompleta sobre a Haste que Passa do Arame
Na falha que sobrou com a parede estimada além da real, a haste encostou no arame por duas iterações e, na terceira, a ponta saltou quatro centímetros para dentro. A haste tinha passado do arame. Achei que fosse o mesmo mecanismo da tentativa desfeita mais cedo: a cada iteração bloqueada, a busca soma o que faltou à mira além do alvo, e na terceira o ombro empurra demais. Limitei essa mira a dois milímetros ao longo do furo.
A bateria não confirmou a ideia. Os saltos não diminuíram: foram cinco em vinte e três atravessas, contra um a três nas baterias anteriores. Olhando cada um, a maioria acontece já na primeira iteração ou de uma iteração para a outra, sem nada acumulado. É o ombro sem encoder passando do pedido, um defeito que já tínhamos medido em outubro. A mira limitada não atua sobre ele. O total caiu de vinte e dois para dezenove, e a mudança ficou como rascunho, com a recomendação de não mergear.
O próximo alvo, se o orientador quiser seguir, é o próprio salto do ombro na chegada à haste.
04 Nov 2026 (tarde)
A Busca da Haste: Vinte e Duas em Vinte e Cinco
Com a parede estimada aquém da real ao longo do furo, a atravessa chegava ao alvo sem tocar em nada, e a haste ficava um a dois centímetros antes do arame. O arame caía perto da ponta do degrau e, quando o destrava empurrava, escapava por ela. Nas execuções sem viés, a sonda mostrou que a ponta já terminava com a haste encostada no arame: é ela, e não a estimativa, que segura o dedo no lugar certo.
A primeira ideia foi mirar quinze milímetros além da haste e deixar o contato parar a ponta. Não deu certo. A busca iterativa acumula a mira além do alvo, isso virou força contra o arame, a ponta oscilou de um lado para o outro e a haste acabou passando dele. Desfiz a mudança. A segunda foi mais modesta: depois da atravessa, avançar cinco milímetros por vez, um comando por passo e sem acumular nada, até a ponta parar de avançar. Ali a haste encontrou o arame, e a estimativa da parede passa a ser a do contato.
Na bateria, a busca encontrou a haste em vinte e duas missões. O viés que vinha falhando foi de dois para cinco acertos em cinco, e o total chegou a vinte e dois em vinte e cinco, o melhor até aqui. A decisão de mergear é do orientador.
04 Nov 2026
A Haste que Para a Seis Milímetros do Alvo
Com a parede estimada oito milímetros além da real ao longo do furo, a haste encosta no arame antes do alvo. A ponta para sempre no mesmo lugar, entre seis e sete milímetros do alvo, e a tolerância da fase é de seis. Faltava um milímetro, e a missão gastava as iterações tentando avançar contra o arame. O engate por contato existia para isso, mas não fechava. Ele esperava o ombro ficar cada vez mais aquém do pedido, o que quase nunca acontecia. E exigia a ponta alinhada justamente no instante em que ela escorregava para cima e para baixo no arame.
O critério novo olha para o que a ponta faz ao longo de três iterações: se ela fica no mesmo ponto do eixo, perto do alvo e do lado de cá dele, com a profundidade e a altura medianas dentro da tolerância, é a haste no arame. A fase fecha ali e a estimativa da parede passa a ser a do contato. Antes de rodar, testei o critério sobre os registros das baterias anteriores. Ele só teria disparado em missões que falharam.
Na bateria, disparou duas vezes, ambas no viés para o qual foi feito, e as duas missões deram certo. O total empatou com a versão principal: o viés oposto caiu, mas as falhas dele aconteceram depois do engate, em fases que a mudança não toca. A decisão de mergear é do orientador.
03 Nov 2026
A Garra na Frente da Tag e o Diagnóstico que Eu Tive de Desfazer
O dia começou desfazendo uma conclusão minha. Na véspera, eu tinha escrito que o ombro às vezes não executava o último passo da atravessa e que o estimador sem encoder não percebia. Comparando a ponta estimada com a verdadeira, as duas coincidiam. O ombro tinha ido aonde foi mandado. O alvo é que estava errado: a parede estava estimada quase quatro centímetros fora ao longo do eixo do furo, a única direção que o laser não corrige.
Medindo em cento e trinta missões, o padrão ficou claro. Quando o refinamento de perto via a tag, o erro ao longo do eixo não passava de três milímetros. Só que ele quase nunca via: em quatro de cada cinco missões ficava valendo a medida feita de longe, com erro de até quarenta e um milímetros. Com mais de vinte milímetros de erro, nenhuma missão deu certo. O registro do localizador mostrava detecções durante a aproximação e silêncio total no ponto de parada. Gravei um quadro da câmera nesse instante, e lá estava: a tag no quadro, com a garra cobrindo a borda de cima dela.
A correção foi inclinar o punho treze graus antes da medida. Errei o sinal na primeira tentativa, porque confiei na cinemática em vez da imagem, e a tag subiu em direção à garra. Com o sinal certo, ela caiu inteira no centro do quadro. Na bateria, o refinamento viu a tag nas vinte e cinco missões, o erro da parede ficou em no máximo três milímetros, as capturas fora do anel sumiram e o total foi a dezoito, o melhor até aqui. A decisão de mergear é do orientador.
02 Nov 2026
O Furo Elíptico e a Margem que Sobrou de Setembro
A atravessa com a parede estimada doze milímetros fora em profundidade não acertava mais o furo: zero em dez. Os contatos da simulação mostraram o que acontecia. O degrau batia na curva de cima do olhal, a lingueta cedia para baixo e o dedo passava por cima do anel. A causa era uma margem de setembro. A entrada mirava oito milímetros para o lado do robô, de quando o refinamento errava a parede por até dezessete milímetros. Hoje o laser corrige a profundidade. O furo é uma elipse, e na altura da entrada o degrau tem uns sete milímetros de folga para cada lado. Sem viés nenhum, a ponta já chegava a cinco milímetros do centro.
Trouxe a entrada para o centro, e a profundidade deslocada foi para três em cinco. Mas a mudança mexeu na fase seguinte. Com o degrau centrado, a captura desce mais fundo e já empurra a lingueta. O destrava chegava ao fim de curso com menos de dez milímetros de descida, e a missão o recusava como curto, mandando reassentar um destrava cumprido. A correção foi contar a descida a partir do alvo da captura quando ela fecha mais funda que ele. Na segunda bateria, essa falha sumiu, e a profundidade deslocada ficou em cinco acertos em dez nas duas baterias. O total empatou com a versão principal.
A falha que sobrou aparece em todas as versões recentes. O ombro não executa o último passo da atravessa, de uns vinte e cinco milímetros, e o estimador sem encoder conta o comando como feito. A ponta fica fora do anel e o destrava desce no vazio. A decisão de mergear é do orientador.
01 Nov 2026
A Lingueta a Um Milímetro da Trava
A perda do olhal na libera tinha uma assinatura clara na sonda. Nas libera que deram certo, a lingueta começava entre dezoito e vinte milímetros; nas três perdas com a parede estimada alta, começava em catorze, e a aba passa sob a trava a treze. Com a estimativa deslocada para cima, a captura terminava com o degrau só encostado no arame, enquanto nas outras ela já empurrava a lingueta alguns milímetros. O destrava descia vinte milímetros a partir dali e parava ao alcançar o alvo. Na libera, o punho cedia, a ponta subia um pouco, a trava fechava de novo e a ponta escorregava pelo furo.
A missão não tem como ver esse deslocamento, porque a captura e a descida estão no mesmo referencial estimado. O que ela vê é a ponta parar quando a lingueta bate no fim de curso. O destrava passou a mirar vinte e oito milímetros e a fechar por essa parada. Na bateria, a libera começou com a lingueta no batente em todas as dezenove execuções engatadas, e a altura deslocada foi de um para quatro sucessos em cinco.
A profundidade deslocada caiu para zero em cinco, mas falhou na atravessa, antes da fase alterada. Repeti: zero em cinco com a mudança, e um em três com a versão principal, no mesmo stack. A mudança não roda antes da captura, então isso é a atravessa instável nesse viés, e é o próximo ponto, junto com duas perdas novas no segundo arco. A decisão de mergear é do orientador.
31 Out 2026
A Entrada em Três Passos e o Empate que Resolveu o Defeito
Na véspera, os registros mostraram que a atravessa falhava na primeira iteração: a ponta ia oitenta milímetros ao longo do furo num comando só, e o ombro sem encoder passava do pedido. A correção foi simples. Antes de chegar ao furo, a ponta para duas vezes, a sessenta e a trinta e cinco milímetros, na mesma profundidade e altura. Cada movimento do ombro fica entre vinte e cinco e trinta e cinco milímetros. Um passo intermediário que não fecha não aborta a missão, que segue para o próximo.
A bateria de vinte e cinco execuções deu dezesseis sucessos, contra dezessete da versão principal. A atravessa sem fechar caiu de seis para três, que era o defeito medido. Quatro dos cinco vieses ficaram iguais ou melhores. A altura deslocada caiu de três para um, mas as falhas ali foram na libera e na captura, sem relação com a entrada, e com cinco execuções por viés duas de diferença cabem na variação.
Um empate no total, com o defeito que motivou a mudança resolvido pela metade. Recomendei mergear, e a decisão é do orientador. A falha mais frequente agora é a perda do olhal na libera.
30 Out 2026
O Dedo v4 e o Salto da Primeira Iteração
O degrau de trinta milímetros entrou na versão principal como peça de verdade: o gerador do dedo ganhou a revisão D, com a malha para impressão e a prancha cotada, e o modelo da simulação passou a desenhar a peça nova, para que o que se vê e o que colide voltassem a ser a mesma coisa. Imprimir é decisão do orientador. Ao recontar as falhas da bateria para fechar o registro, achei um erro meu: a atravessa sem fechar tinha acontecido seis vezes, não cinco, e uma linha da tabela que eu tinha mostrado não existia. Corrigido numa PR só de documentação.
A atravessa que não fecha virou o alvo. Nas seis falhas, a ponta saltava dezenas de milímetros entre iterações ou alternava de lado. Juntei três correções: um limite para a mira além do alvo, que acumulava sem freio; um amortecimento do ganho quando o erro troca de sinal; e o sentido do recuo por contato, que avançava em vez de recuar e estava corrigido havia dois dias numa PR que nunca chegou à versão principal. A bateria deu onze de vinte e cinco, pior que os dezessete da véspera.
O motivo apareceu nos registros: o salto acontece na primeira iteração, antes de existir mira acumulada para limitar ou troca de sinal para amortecer. A atravessa leva a ponta oitenta milímetros ao longo do furo num comando só, e o ombro sem encoder passa do pedido. O limite e o amortecimento só atuam depois que o dano aconteceu. A PR ficou como rascunho, com a recomendação de não mergear, e o próximo passo é dividir a entrada em passos menores.
29 Out 2026
O Olhal Medido, a Libera que Não Sobe e o Degrau Mais Longo
O orientador voltou do laboratório com o paquímetro: o arame do olhal tem oito milímetros de diâmetro e o furo, quarenta por vinte e oito. A primeira anotação na foto dizia vinte de largura; ele mesmo duvidou, voltou ao olhal e remediu. Com vinte, metade das conclusões da semana estaria errada; com vinte e oito, o modelo da simulação, que usava trinta por quarenta e um arame de seis milímetros, estava quase certo. O anel foi corrigido e uma bateria de referência rodou de madrugada: dez de vinte e cinco, o melhor total até então, com um buraco claro. Com a parede estimada dez milímetros alta, nenhuma execução abriu a chave, e em quatro a libera “subiu”.
O defeito era conhecido desde o procedimento pela lateral, na terça: a libera puxava numa altura fixa, e o destrava terminava mais embaixo, então puxar também subia a ponta e a aba voltava para o laço. Trazido sozinho para a versão principal, o pedaço que faz a libera puxar na altura em que o destrava terminou levou aquele caso de zero para quatro de cinco e o total para catorze de vinte e cinco. Foi mergeado.
O caso que caiu para zero foi o da parede estimada treze milímetros mais perto: nas cinco o destrava desceu sem o olhal, porque a ponta do degrau parava antes dele. Era a hora do degrau mais longo que o orientador tinha perguntado no dia anterior. Com trinta milímetros em vez de vinte, a bateria da madrugada deu dezessete de vinte e cinco: o controle fechou cinco de cinco, e o caso do degrau curto foi de zero para três. Para a bancada isso significa imprimir uma peça nova, e essa decisão é dele. O que sobra, agora, é quase só uma falha: a atravessa que não fecha.
28 Out 2026
O Braço que Caçava o Alvo e o Dedo do Outro Lado do Olhal
Começou de madrugada, com a bateria dos dois milímetros a mais de inserção terminando em seis de vinte e cinco, contra oito de vinte e cinco sem eles. Empate dentro da variação: com cinco execuções por caso, duas missões de diferença não dizem nada.
Durante a bateria o orientador viu duas coisas. A primeira foi o dedo indo parar do outro lado do olhal: na atravessa a IK pediu ao ombro uma correção de seis décimos de grau e o braço, sem encoder, girou quatro ou cinco; a ponta andou quarenta e oito milímetros numa iteração. A segunda foi o braço tremendo. Um ensaio controlado explicou o tremor: perto do alvo o servo manda a junta no mínimo de três graus por segundo, porque abaixo disso o motor não vence o atrito, e a medida da ponta chega dois décimos de segundo atrasada. Nesse tempo a junta passa meio grau do alvo, cinco milímetros na ponta, mais que a tolerância de três. O comando invertia a cada fração de segundo, e o ombro ficava indo e voltando por quatro segundos até parar por acaso. A correção foi deixar a junta descansar quando ela cruza o alvo, e fechar a postura se a ponta estiver dentro do menor passo que o braço consegue dar. O vai-e-volta caiu quatro vezes nas fases de postura, sem mudar a taxa de sucesso.
Para o dedo do outro lado, duas mudanças: voltar ao alvo quando a ponta aparece muito além dele, e um defeito antigo no recuo por contato, que tirava o sentido do sinal do erro e, com a ponta além do alvo, avançava mais vinte milímetros em vez de recuar. A volta salvou o único salto verdadeiro da bateria seguinte, mas o total ficou nos mesmos seis de vinte e cinco.
À tarde o orientador perguntou se um degrau mais longo ajudaria. Os dados de ontem sugerem que sim, para os casos em que a ponta do degrau para alguns milímetros antes do olhal. Alonguei o degrau de vinte para trinta milímetros só na física da simulação, e o orientador estranhou que nada mudava na tela, até ligar a vista das colisões. O teste rápido abriu a chave no caso nominal e falhou no caso difícil; a bateria completa ficou para depois, porque ele foi para o laboratório.
27 Out 2026
O Desenho que Levou o Dia e o Destrava pela Lateral
O dia começou com um pedido simples: desenhar como o dedo entra no olhal, para o orientador corrigir onde eu tinha entendido errado. Levou quinze versões. A primeira vista de lado escondia quais eram as faces de cima e de baixo; a primeira vista em três dimensões parecia ter o plano do olhal atrás do dedo; a vista frontal ficou pior; o desenho técnico em três vistas ficou difícil. O que destravou foi uma frase dele: a haste tem que estar paralela ao eixo Y. Eu tinha desenhado a haste na vertical em todas as figuras. Com o degrau ao longo de X e a haste vindo do robô ao longo de Y, a Figura 6 virou a referência, e as respostas vieram por número: a face inferior do degrau encosta no fundo do olhal no destrava, o arame tem de três a cinco milímetros de raio, o que para o dedo quando ele avança demais é a haste, e não existe batente ao longo do furo. E uma que eu não tinha entendido: destravar não é abrir. O destravamento é o movimento para baixo; a abertura vem depois.
No meio disso, o “teto” que aparecia nos meus relatórios virou “limite de descida do destrava”, com explicação: quando a ponta desce vinte e dois milímetros sem a lingueta parar, o degrau nem entrou no furo, desceu no ar na frente do olhal. A recuperação que eu tinha feito, avançar em X e tentar de novo, não mostrou ganho, e a bateria foi interrompida duas vezes, uma pela parede movida por engano no simulador, outra porque o orientador descreveu um procedimento melhor.
O procedimento dele, pensado para um braço sem encoder: o degrau no centro do olhal; daí andar para longe da parede até a face lateral do degrau encostar no arame; com ela encostada, descer e depois puxar, sem subir. A sonda mostrou que funciona como desenhado, a face encosta, a lingueta chega ao fim de curso e fica embaixo enquanto a lâmina abre, o que antes não acontecia. A bateria completa deu oito de vinte e cinco. Com dois milímetros a mais de inserção, que ele sugeriu olhando a simulação, o controle fechou cinco de cinco. O caso em que a missão acha a parede treze milímetros mais perto continua sem solução.
26 Out 2026
O Teto que Dizia “Não Engatou” e a Reentrada que Repetia o Erro
O orientador escolheu, das três frentes abertas, a libera depois de um engate fora do centro, que era onde a medição de sábado apontava a maioria das perdas. O primeiro passo foi separar as perdas das quarenta e cinco execuções medidas e olhar, pela verdade do Gazebo, o estado do engate no instante em que a libera começava. A resposta mudou o alvo: em quase todas as perdas, a lingueta já estava em zero quando a libera começou. O dedo não estava mais segurando o anel. A perda não acontecia na libera; acontecia antes, no destrava.
E o destrava deixava rastro. Ele fecha de duas maneiras: por estagnação, quando a ponta para porque a lingueta chegou ao fim de curso, ou pelo teto de descida, quando a ponta desce vinte e dois milímetros sem parar. Em dezessete execuções fechou pelo teto, e quinze abortaram depois. Em trinta e oito fechou por estagnação, e trinta e três abriram a chave. Chegar ao teto sem o mecanismo parar é a ponta descendo sem o anel, e a missão tratava isso como fase cumprida. O sinal é a descida da ponta medida pela câmera de punho, o mesmo na bancada.
A correção óbvia foi tratar o teto como falha e reassentar: sair do furo pelo eixo, reentrar pela lateral, recapturar e destravar de novo. Vinte e cinco execuções de validação, cinco por viés, e cinco sucessos. O teto disparou em nove execuções e só uma se recuperou. As outras oito repetiram o erro, porque a reentrada usa a mesma estimativa da parede que tinha levado o dedo para fora do anel na primeira vez. A PR ficou como rascunho, com a recomendação de não mergear. O diagnóstico fica: o critério antigo de sucesso do destrava estava errado. O que resolveria é usar o teto para corrigir a estimativa, e a direção da correção depende da geometria do degrau dentro do furo, que é o mesmo ponto em aberto do viés de menos treze milímetros. Antes de mais código, um desenho com o orientador.
No meio da tarde ele fechou todas as janelas e a simulação sumiu da tela. Reabri as quatro, Gazebo, RViz, câmera e painel, e o RViz voltou sem o robô, porque eu o tinha aberto sem o ambiente do workspace. Reaberto do jeito certo, o robô apareceu.
24 Out 2026
Quarenta Execuções e um Número Honesto
A bateria grande pedida pelo orientador terminou de madrugada: oito vieses da parede, cinco execuções cada, intercaladas para que nenhum viés pegasse só um momento bom do simulador. Dezoito de quarenta. Sem viés, cinco de cinco; com a parede dez milímetros baixa, cinco de cinco também, e esse é o único resultado da semana que dá para atribuir a uma mudança, a descida do destrava contada desde a entrada no furo em vez de desde a captura. Os outros seis vieses ficaram entre zero e dois de cinco.
Os dados da bateria mostraram um defeito no engate por contato: numa execução o dedo travou quatro vezes no mesmo ponto, a nove milímetros e oito décimos, e o engate nunca foi aceito, porque a primeira trava, de antes de o dedo assentar, entrava no cálculo do espalhamento. Corrigido para olhar só as últimas duas. Cinco execuções de confirmação: o engate passou a disparar em três, mas as perdas mudaram de lugar e foram para a libera. No dia, o viés que a PR anterior resgatava três de três ficou em três de dez. A PR continua como rascunho, com a recomendação de não mergear, e a lista do que tem efeito medido cabe numa linha.
23 Out 2026
A Janela de Um Pixel e a Sonda que Empurrou para Fora
O orientador mergeou a PR da varredura lateral e perguntou por que eu falava em stack se ele não via nada. Ele tinha razão: a interface do Gazebo rodava havia seis dias com uma janela de um por um pixel, e todas as baterias desde o dia dezessete tinham rodado sem imagem. Fechei só a interface e abri outra, com o simulador de pé, e no fim do dia derrubei o stack a pedido. Ele também avisou que não tem como medir corrente de nenhuma junta, o que descarta a sugestão de hardware da véspera.
À tarde, de volta à simulação, duas mudanças. O detector de contato caótico passou a exigir a ponta saltando de verdade, não só o erro parado acima da tolerância. E uma sonda de inserção: depois da atravessa, empurrar a ponta até o canto do degrau, para corrigir a parede quando ela estivesse fora no eixo. O teste rápido refutou a ideia: no viés de menos treze milímetros, a verdade do Gazebo mostrou o dedo já na posição de uma captura boa ao fim da atravessa, e a sonda o empurrou vinte milímetros para fora. A hipótese que tinha motivado a sonda, dedo raso na atravessa, estava errada. A sonda ficou desligada. Dois tropeços meus no caminho: subi o stack sem a garra fechada e sem o braço em malha aberta, e depois disse que a bateria grande estava rodando quando o script nem tinha permissão de execução.
22 Out 2026
Três Baterias e Nenhuma Conclusão: Quando a Variação Engole o Efeito
Um dia inteiro de simulação, da manhã até depois da meia-noite, e uma PR aberta como rascunho com a recomendação de não ser mergeada. O orientador disse “segue em frente” sobre as pendências de ontem, e eu segui, até a terceira bateria mostrar que o resultado não sustentava nenhuma das mudanças.
Sete mudanças, quatro ficaram
A primeira foi o detector de contato caótico: quando o dedo escorrega pela borda do furo a ponta pula de um lado para o outro, a assinatura de “não avança” nunca se repete, e as iterações acabam antes de a varredura chegar ao passo que entra. Três iterações sem fechar passaram a valer como travamento. A segunda veio de reler o bag da falha de ontem com altura +10: o escorregão que o detector tinha visto era falso, a lâmina tinha girado de três para trinta e dois graus durante o puxão, com o dedo engatado, e a ponta foi junto pelo arco. A terceira foi contar o curso do destrava desde a entrada no furo, não desde a captura. As três foram defensáveis, e a terceira resolveu a execução em que disparou.
A bateria seguinte trouxe três achados, e eu respondi com mais três mudanças. Nenhuma sobreviveu: o detector de “preso por trás do degrau” disparou com o dedo fora do anel; a ideia de tratar o engate por contato como hipótese que a captura confirma não confirmava nada, porque a captura se move junto com a parede deslocada; e o destrava mais fundo empurrou o dedo quarenta milímetros para baixo fora do anel. Reverti as três.
O que a terceira bateria disse
Com o estado final medido, três de sete, três de oito, dois de oito. O mesmo viés passava numa execução e falhava na seguinte, em todas as baterias. Com uma ou duas execuções por caso, a variação entre execuções é maior que o efeito de qualquer mudança, e não dá para afirmar que alguma delas melhora o resultado. Escrevi isso no relatório e na PR.
A lição técnica é uma só: tudo que tentou decidir, pela estimativa, se o dedo estava dentro ou fora do anel falhou, porque o ombro sem encoder erra um grau, que na ponta são dez milímetros, exatamente a escala da diferença. Os sinais que funcionaram são os que não dependem dessa precisão. Na bancada, o sinal que falta é a corrente nos motores do ombro e do cotovelo durante o destrava, que diria diretamente se o anel resiste. A lição de processo é outra: para medir efeito de mudanças desse tamanho, cinco execuções por caso, ou uma mudança por bateria.
21 Out 2026
A Varredura que Funcionou, o Dedo que Já Estava Dentro e o Contato que Passou a Mandar
Tarde e começo de noite de simulação, uma PR aberta e depois ampliada, e uma bateria que respondeu à pergunta feita e a uma outra, maior, que ninguém tinha feito.
O candidato de sexta
As três PRs de sexta tinham sido mergeadas na mesma noite, e a pendência que ficou em simulação era o aborto da quarta execução daquela bateria: a atravessa travando duas vezes no mesmo ponto, com os dois recuos de contato, que só desciam cinco milímetros, sem mudar nada. O orientador pediu a varredura lateral. Cada recuo passa a mirar o alvo original deslocado no plano do anel por uma lista de pares, profundidade e altura: o “desce cinco” de sempre, depois oito milímetros para cada lado nessa altura, depois cinco para cima. O recuo pelo eixo do furo já leva a ponta para a nova posição, para reentrar alinhada. E, para validar sem depender da sorte da percepção, um parâmetro que só existe na simulação soma um viés conhecido à parede depois da medição fina.
Sete vieses, dois sucessos
A bateria foi uma execução sem viés, duas com doze milímetros de profundidade para cada lado, duas com dez de altura, duas com treze no eixo do furo. A varredura fez exatamente o que foi feita para fazer: com doze milímetros de profundidade para o lado da borda, o dedo travou, os dois primeiros recuos não mudaram nada, o terceiro entrou, e a chave abriu. Sem viés, nenhuma regressão. As outras cinco abortaram, e a sonda de contato mostrou que em três delas o dedo estava dentro do furo, engatado, e a missão abortou por acreditar na própria estimativa contra a evidência do contato.
O que a sonda contou sobre sexta
Reler a execução de sexta pela verdade do Gazebo mudou a leitura. O degrau estava a oito milímetros do anel, a mesma profundidade de uma captura boa, enquanto a missão acreditava que faltavam vinte. A parede da medição fina tinha saído treze milímetros fora ao longo do eixo do furo, a única direção que o laser não corrige, e o “não avança” era o dedo já engatado empurrando o anel contra o canto do degrau. A varredura trata profundidade e altura; a execução com treze milímetros no eixo reproduziu sexta ponto por ponto, quatro recuos no mesmo lugar. Para o outro lado do eixo o dedo engata raso e perde na libera, que é a perda da semana passada. Nos erros de altura, para baixo o dedo comprime a lingueta antes da hora e o critério do destrava, que conta a descida desde a captura, não enxerga o que já desceu; para cima o dedo roça no arame superior e gasta as cinco iterações oscilando antes de o detector de contato disparar. E para o outro lado da profundidade o dedo entra, mas a captura exige seis milímetros em relação a um alvo doze fora, e o anel segura o dedo.
Quatro propostas e a primeira delas no mesmo dia
Quatro propostas no relatório, todas da mesma família: deixar o contato mandar quando ele contradiz a estimativa. Tratar o bloqueio repetido perto do alvo como engate e zerar o eixo da captura no ponto do contato; fechar a captura por contato em profundidade; contar o curso do destrava desde a entrada no furo, não desde a captura; e separar o orçamento de iterações do dos recuos. O orientador mandou seguir com a primeira.
A primeira versão esperava a varredura inteira antes de decidir, e a bateria mostrou por que isso era errado: com treze milímetros de viés no eixo, o passo lateral de oito milímetros fez o dedo escorregar dez milímetros além do canto do degrau, a captura fechou com o arame no fim do degrau e a libera perdeu o anel. A geometria resolve sem esperar: a borda do furo bloqueia a dezesseis milímetros e meio do alvo, dez de inserção mais o raio do arame, e isso não depende do viés; o canto do degrau bloqueia a viés menos dois. Decidir na segunda trava, com teto de quinze, separa os dois casos e evita o passo que afunda o dedo. Na bateria seguinte as duas execuções com treze milímetros no eixo engataram por contato na segunda trava, com a parede deslocada onze milímetros e a chave aberta a trinta e um e trinta e dois graus, e a execução com a borda do furo foi recusada pelo critério, como devia. Duas falhas de outra natureza ficaram anotadas: um contato caótico na borda que escapa do detector de “não avança”, e a saída pelo eixo divergindo quando o arame segura o dedo. E, de brinde, a execução de controle trouxe a primeira recuperação completa do escorregão da libera pela saída do furo, implementada na sexta e nunca exercitada até hoje.
17 Out 2026
O Arame no Meio do Degrau, o Raio que Não Era Duzentos e a Borda do Furo
Manhã curta, duas PRs abertas. O orientador perguntou, de casa, quais dos três pontos pendentes eu conseguia atacar sem o robô, e a resposta foi “dois”: as propostas da sonda de ontem, que se validam em simulação, e a lacuna do firmware que o tutorial de bancada tinha exposto. O terceiro, a bancada em si, é dele.
Quatro baterias para três correções
A sonda de ontem tinha mostrado o anel escapando no fim do puxão da libera, com o arame dois a três milímetros mais perto da quina do degrau do que nas execuções boas. A primeira correção foi óbvia: inserir dez milímetros em vez de oito, com tolerância de quatro, para o arame ficar no meio dos vinte milímetros do degrau. A segunda, detectar pelo T265 a subida súbita da ponta, mais de oito milímetros em três décimos de segundo, que é o que o robô real enxerga, e reassentar. A bateria que validaria as duas falhou noutro lugar: libera e primeiro arco limpos, e o dedo saltando no fim do segundo arco, com a lâmina no batente e o controlador ainda empurrando. A sonda explicou: o anel vai pressionado uns vinte milímetros para baixo na lingueta durante o arco, de modo que a circunferência que ele descreve tem raio efetivo de cento e oitenta milímetros, não duzentos. O ponto a trinta e três graus nominais exigia a lâmina a trinta e sete, além do batente. O arco passou a terminar em trinta graus nominais.
A bateria seguinte trouxe o defeito da tolerância: a captura oscilava no eixo entre menos sete e mais doze milímetros, cinco iterações, enquanto o servo dizia “postura alcançada”. Sem encoder, a estimativa da primeira junta erra cerca de um grau, que a sessenta centímetros de alcance são dez milímetros, e a correção seguinte passa para o outro lado. Quatro milímetros de tolerância está abaixo do que o estimador dá; ficou em seis. E a terceira bateria foi a primeira em que o detector disparou num escorregão real, dez milímetros em três décimos, para mostrar que a recuperação herdada não serve: subir vinte e cinco milímetros e atravessar de novo só empurra o degrau contra o arame por cima, porque depois do escorregão o arame está em cima do degrau, não embaixo. A recuperação passou a sair do furo pelo eixo, com os mesmos degraus do aborto, e reentrar pela aproximação lateral, como na primeira vez.
Cinco engates limpos e um aborto que não era do engate
Com as três correções juntas, seis execuções: cinco abriram a chave, entre trinta e um e trinta e quatro graus, e a sonda não mostra em nenhuma delas o que mostrava ontem. O arame fica a oito a doze milímetros da quina durante a libera inteira, a lingueta se mantém nos vinte milímetros até o desengate, e o contato com a haste da ferramenta, que ontem era a assinatura da perda, aparece só de passagem. O detector de subida não precisou disparar, o que deixa a recuperação nova implementada mas sem uma execução que a exercite. Fica como seguro para a bancada.
A sexta execução abortou antes de tocar na chave, e o motivo é outro capítulo: a tag da medição fina saiu treze milímetros fora no lateral, o laser corrige profundidade e yaw mas não lateral, e o dedo travou na borda do furo oval, a dez milímetros do centro, onde a folga é de uns dez milímetros. A missão detectou o contato, recuou duas vezes, esgotou as iterações e saiu em degraus sem derrubar nada. Comportamento correto, mas o próximo ponto fraco visível: quando a atravessa trava no mesmo ponto duas vezes, varrer alguns milímetros para os lados antes de desistir.
As placas que não contavam o batente
A outra PR fecha a lacuna do tutorial reescrito ontem. O firmware de velocidade cortava o sentido ao chegar no fim de curso, mas não dizia isso ao ROS, e tanto o homing quanto a âncora do estimador dependem desse sinal. Cada placa Arduino passa a publicar, junto do status, um vetor de menos um, zero ou mais um por junta, já no sinal do modelo, e um nó de ponte junta os três tópicos no mesmo formato que o firmware emulado da simulação publica. Servo e estimador não distinguem bancada de simulação. Os três sketches compilam; gravar nas placas e conferir os sinais no primeiro homing devagar é trabalho de bancada, e decisão do orientador.
16 Out 2026
O Controlador que Trancava, as Oito Partidas e o Anel que Sobe pelo Degrau
Três PRs mergeadas e uma aberta, numa sequência em que cada bateria terminou apontando o defeito seguinte. O dia começou pela pendência que ficara de ontem, a orientação da ferramenta nas fases em que o dedo está no anel, e terminou com uma sonda de contato mostrando, a vinte hertz, o instante exato em que o anel escapa.
A atitude que ficava à sorte
As fases whole-body, destrava, libera e arcos, eram só de posição: cinco graus de liberdade não fecham uma pose de seis, e a tarefa especifica direções de força. A consequência apareceu ontem, quando o desengate por cinemática inversa re-alinhou o degrau com um giro de punho de vinte e cinco graus com o dedo ainda no anel. A solução foi tratar a orientação como preferência: o alvo passa a carregar a atitude do T265 registrada no engate, e o controlador acrescenta as três linhas angulares à Jacobiana com peso baixo, de modo que a posição manda. Na primeira bateria o desengate oscilou trinta segundos entre dois pontos: com a base travada o braço não consegue sair noventa milímetros de lado mantendo a atitude completa, e a preferência brigou com a posição. O desengate ficou sem orientação, por fase. A bateria seguinte fechou cinco de cinco, com erro angular máximo de cinco graus.
Oito partidas, dois defeitos antigos
O orientador perguntou se eu queria começar a bateria de partidas, e a resposta eram as mesmas oito poses de setembro, duas vezes cada. Percepção e navegação fecharam vinte e quatro de vinte e quatro, incluindo a partida de frente que em setembro era zero de três. As quatro falhas foram todas de manipulação, e expuseram duas coisas que a pose de referência nunca tinha mostrado. A primeira era um trinco no controlador: no modo de ponta a flag de “alcançado” virava verdadeira quando a ponta passava a menos de dois milímetros do alvo num transiente, e não tinha caminho de volta. O bag de uma das falhas mostra as juntas congeladas por trinta segundos, a base sendo arrastada três centímetros e meio para a parede pelo contato, e o registro do controlador repetindo “alvo alcançado” a cada dois segundos. A segunda era o fim do arco: a lâmina para em trinta e quatro graus e quatro décimos em todas as execuções, que é o batente dela, e o alvo a trinta e cinco graus fazia o whole-body empurrar contra o batente até a régua fechar. Duas vezes o dedo escapou antes. Histerese de volta no controlador, arco terminando em trinta e três graus, e a confirmação nas quatro poses que tinham falhado fechou sete de oito.
O anel que sobe pelo degrau
A falha restante era uma perda do engate logo na libera, sem correlação com o ângulo do degrau nem com a postura do braço. Montei uma sonda só de simulação: a ponta da ferramenta, como o controle a vê, contra o centro do anel pela verdade do Gazebo, a vinte hertz, mais os contatos da ferramenta filtrados do tópico de física. Quatro execuções na pose que mais perdia, duas perdas, e as duas idênticas. No fim do puxão da libera, com a lâmina a sete graus e meio, o arame corre três milímetros pelo degrau em direção à quina, a lingueta volta de vinte para seis milímetros, e o anel sobe catorze milímetros levando o dedo junto. O arame passou por cima do degrau. Dali em diante o contato inclui a haste, o controlador segue pressionando o anel pela haste, e no segundo arco o anel desliza até cair da ponta. Nas duas perdas, a captura tinha deixado o arame dois a três milímetros mais perto da ponta do degrau do que nas execuções boas, dentro da tolerância de eixo da captura, que não controla essa margem. Três propostas ficaram no relatório para o orientador decidir: detectar a subida da ponta pelo T265 e reassentar, que é o que o robô real enxerga; inserir mais fundo com tolerância mais apertada; ou puxar mais devagar.
O que fica
O tutorial de bancada foi reescrito no meio do dia, porque o anterior descrevia um braço com encoder que não existe. Ganhou a tabela dos fins de curso para conferir à mão, um ensaio novo para o primeiro homing real, devagar e na ordem certa, e a medida de quanto o robô translada num giro parado. E uma lição de processo repetida duas vezes: interromper uma bateria no meio deixa o gravador de rosbag vivo, e o commit da pasta falha até alguém matá-lo.
15 Out 2026
Os Limites que Escondiam a Borda do Espaço de Trabalho, o Arco que Desliza e o Giro que Translada
O dia começou à meia-noite, na retomada combinada, e terminou com quatro PRs mergeadas. O modelo do braço tinha ganhado os limites do robô real na véspera, e a primeira bateria com eles tinha abortado no destrava. O que faltava era fazer o controle saber dos limites, e depois descobrir o que mais os limites antigos escondiam.
Saturação explícita e uma barreira que escolhia postura
Com os limites reais a cinemática inversa trabalhava encostada no cotovelo reto e no punho no switch, e a pseudo-inversa do whole-body seguia distribuindo velocidade para juntas que o firmware não deixa andar. A ponta derivava pelo eixo do furo em vez de descer. Duas correções: um resolved-rate ciente da saturação, em que a junta no limite com velocidade pedida para fora perde a coluna da Jacobiana e as outras compensam, e uma barreira na cinemática inversa da tarefa. A primeira versão da barreira agia em todo o curso, gastou o grau de liberdade livre da tarefa, que é a rotação da haste em torno do eixo do furo, inclinou a haste doze graus para dentro da parede e a fase de atravessar o furo caiu. Com zona morta de seis graus a barreira só tira a junta do switch por alguns graus, sem escolher postura. A bateria seguinte fechou quatro de cinco, e a falha restante não era do braço: o alvo do destrava pedia eixo zero com a ponta capturada a oito milímetros para dentro do furo, de propósito, e a régua de dez milímetros decidia a missão no cara ou coroa desde a semana anterior. Alvo relativo à captura, cinco de cinco. O orientador decidiu ainda que a home position e a postura de acordar ficam com o ombro na vertical: parar com o braço superior perto da horizontal é risco que não vale a pena.
A distância que o modelo antigo tinha escolhido
De manhã, a pendência era o pré-posicionamento com o punho no switch. Varrendo o standoff por cinemática inversa, a folga do punho cresce linearmente com a distância da base ao olhal: seis graus a sessenta e dois centímetros, vinte a sessenta e oito. O degrau que tinha levado o standoff a sessenta e dois em setembro era do modelo antigo, com o cotovelo de sessenta graus para cada lado. Standoff a sessenta e oito, pré-engate a dez centímetros da parede, cinco de cinco.
O arco que desce antes de avançar
O orientador viu que no segundo arco o movimento começava com um mergulho igual ao do destrava. O alvo do arco era o ponto final da circunferência, dois centímetros mais baixo, e o whole-body ia em linha reta: o braço resolvia a altura num ciclo e a base levava segundos. O alvo passou a deslizar pela circunferência da lâmina, quinze milímetros à frente da ponta medida pelo T265. Chegar lá expôs quatro coisas que o recuo prévio com as rodas escondia, cada uma com o seu commit: o recuo em si, que fazia o arco na horizontal com o braço parado; o fim do arco, que precisou passar de trinta para trinta e cinco graus porque a régua fecha três graus antes; o eixo dos arcos e da libera, que passou a herdar o da captura; e o desengate por cinemática inversa, que re-alinhava o degrau com um giro de punho de vinte e cinco graus com o dedo no anel e numa execução arrastou a lâmina de volta a zero. Quatro de cinco, com a quinta perdida pela navegação.
O giro que translada
Essa quinta execução abriu o último assunto do dia. No bag, a base não gira em torno do próprio centro: gira em torno do centro de gravidade, que com o braço fica catorze centímetros à frente, e cada giro parado translada o chassi. Oito centímetros em trinta graus, vinte e dois em cento e trinta e quatro, vinte e um em cem. O giro inicial de alinhamento deixou dez centímetros de erro lateral, o avanço curto com a rotação saturada entrou em órbita em torno do alvo, e a meia-volta para “realinhar” custou duas vezes vinte centímetros. A navegação deixou de dar meia-volta quando passa ao lado do alvo dentro de doze centímetros, e o giro perto do alvo ficou limitado. Cinco de cinco, como teste de não-regressão, porque o evento é raro. Na bancada, a primeira medida é quanto o Pioneer com o braço translada num giro de noventa graus parado.
14 Out 2026
A PR que Já Estava Mergeada, o Zero que Estava na Dissertação e o Braço que Trabalha no Batente
O dia era para ser de bancada: a NUC atualizada e o orientador medindo o ângulo do switch de trás da junta dois, a pendência que ficou de quinta. A bateria da NUC estava descarregada e a medição não tinha como ser feita a olho. O que saiu do dia foi outra coisa: um erro meu de processo com dois dias de trabalho, a resposta à pergunta do zero da junta dois num lugar onde ninguém tinha olhado, e um modelo do braço que, pela primeira vez, tem os limites do robô que existe.
Dezesseis commits que ninguém mergeou
Ao abrir o arquivo de switches do braço para começar a correção, ele estava com o conteúdo antigo. A PR #90 tinha sido mergeada no dia oito, catorze minutos depois de aberta, com três commits. Eu continuei empurrando na mesma branch por dois dias, dezesseis commits, o homing das cinco juntas, os dois estados de home, as duas baterias de cinco, e reportando tudo como “está na PR #90”. Nada disso tinha entrado na main. O push numa branch já mergeada não falha e nada avisa. Recuperei os dezesseis commits numa branch nova e abri a PR #91 só com eles. A regra que ficou gravada: conferir o estado da PR antes de cada push, e conferir que a branch remota não tem nada além da main antes de apagá-la.
O zero estava na Tabela 3
O orientador mandou uma foto do atuador e do sensor da junta dois, com o braço superior a noventa graus do chão, e a instrução de procurar na dissertação do Cerbaro, que fez o retrofit do braço, onde ficam os sensores. Estava lá, em duas tabelas. A Tabela 2 dá os limites de trabalho do braço modernizado: cintura mais ou menos cem graus, ombro de menos trinta a cem, cotovelo de menos cento e vinte a zero, punho mais ou menos cem. A Tabela 3 dá a configuração home, onde o homing acha cada switch: ombro em cento e seis graus, cotovelo em menos cento e vinte e três. A convenção de zero é a mesma do nosso modelo, com o braço superior horizontal à frente em zero e vertical em noventa. O switch de trás fecha dezesseis graus depois da vertical, que é o que a foto mostra. E o cotovelo só dobra para um lado: o ramo do cotovelo que o estimador tratava desde a semana passada não existe no robô. O orientador confirmou: os switches ficam nas posições originais do Cerbaro.
O modelo inteiro passou para esses números. Batentes do URDF nos switches, limites de trabalho da cinemática onde cada switch fecha, a home na configuração home da dissertação com a cintura e a rolagem em zero, o estimador sem o ramo. Conferi por cinemática inversa que a captura no olhal ainda fecha, com dez graus de folga no punho. Com os limites antigos a solução dobrava o cotovelo para trinta e um graus positivos, o lado que o robô não tem.
O cotovelo que dobrou para dentro da base
Na primeira missão com os switches novos o orientador viu o movimento do cotovelo bater no próprio manipulador, e apontou que a posição do punho também contribuía. Tinha razão nas duas coisas. O homing levava o punho ao switch, com a ferramenta apontada para o braço superior, e dobrava o cotovelo até o switch com o ombro ainda baixo: o antebraço entrava na base. Varri a sequência por cinemática direta a partir de quatro posturas de partida e a ordem segura é ombro para trás primeiro, depois o punho ao switch e de volta a zero, só então o cotovelo. O ponto mais baixo da sequência subiu de vinte e um para quarenta e sete centímetros acima da base do braço. A mesma ordem vale para a bancada. Na segunda missão o homing fechou limpo, cinco switches em noventa segundos.
A missão que abortou no destrava
A mesma missão abortou no destrava, e o motivo é o que interessa. Com os limites reais, o braço trabalha encostado nos batentes nas fases de manipulação: o punho no switch durante o pré-posicionamento e a orientação, o cotovelo reto, no switch de cima, da aproximação lateral até a captura. O servo reportou postura bloqueada a três a cinco milímetros em todas. No destrava a ponta desceu dezessete milímetros dos dezoito exigidos e o erro no eixo do furo ficou parado em onze milímetros e meio, com régua de dez. Olhando a bateria boa de quinta, o eixo já estava em nove milímetros, só que a fase fechava pelo teto de descida. A margem era zero antes também, e os limites antigos a escondiam. Existe solução com folga, a cinemática inversa a encontra quando parte da postura de deploy, mas a missão chega já encostada no punho e escorrega para o ramo do cotovelo reto.
O plano para a noite é o que o robô real exige de qualquer forma: a Jacobiana do whole-body e do servo passa a saber que uma junta no switch não anda para fora, e as outras compensam; e a cinemática inversa ganha um termo que afasta a solução dos limites quando a tolerância do degrau deixa folga. Antes disso, uma decisão do orientador: a home position e a postura de acordar passam a ter o ombro na vertical, noventa graus, porque parar com o braço superior perto da horizontal é um risco que não vale a pena.
O que fica
Três lições. Uma branch com PR não é um lugar seguro para continuar trabalhando sem olhar a PR. A resposta a uma medição que não dá para fazer pode estar num documento que já está na pasta de downloads. E um modelo com limites errados não erra só nos ângulos: ele esconde que a tarefa está na borda do espaço de trabalho do braço, e isso só aparece quando os limites viram os do robô que existe.
09 Out 2026
O Home que Não se Via, a Cintura que Ninguém Media e a Bateria sem Muleta
Uma tarde com o orientador dirigindo a simulação pergunta a pergunta, e cada pergunta derrubando uma suposição minha. No fim, onze missões completas com o fluxo que a bancada vai usar, onze aberturas da chave, e a PR #90 pronta para revisão com tudo isso dentro.
“Como você sabe que está em zero no J1?”
O pedido inicial era simples: uma missão nova, parar quando o braço estivesse em HOME e esperar. Fechei tudo, subi o stack limpo e reportei as cinco juntas, com a cintura em zero. A pergunta veio em seguida: como eu sabia disso, se ninguém tinha visto a cintura se mover para encontrar um switch? Não sabia. O zero era a verdade do Gazebo. Eu tinha deixado a cintura e a rolagem do punho fora do homing com o argumento de que a câmera T265 as observa, e isso vale na simulação, onde câmera e odometria compartilham o mesmo referencial por construção. No robô, a guinada da T265 é relativa a onde ela própria acordou, e isso depende de onde a cintura estava ao ligar. Sem switch, o zero da cintura é inventado, e a missão inteira mira a chave a partir de uma cintura que ninguém mediu.
As cinco juntas passaram a fazer homing, a cintura por último, com o braço já recolhido, procurando o switch de trás, e o punho dando meia-volta até o seu. O reset entre missões passou a acordar o braço com a cintura a trinta graus e a rolagem a quarenta, para o homing ser de verdade. Cinquenta e nove segundos para achar os cinco switches, com a estimativa ancorada a menos de um grau da verdade em todas.
Home position é um ponto, homing é um processo
A observação seguinte redefiniu o estado. Se a cintura chega ao switch em cento e cinquenta graus, na home position ela deve estar em zero: os switches são só a referência, a posição de descanso é outra. E, logo depois, que são dois estados: o homing, que é um processo, e a home position, que é um ou zero. A missão ganhou os dois, HOMING e HOME_POSITION, e o painel passou a mostrar o primeiro em amarelo do começo até o braço chegar à posição, e o segundo apagado até ser atingido. A missão termina passando pelos dois, para a próxima partir de onde esta acabou sem supor nada.
Onze missões, e uma auditoria
A missão completa fechou a chave a trinta e um graus, com cinco minutos de missão e um de homing. A bateria de cinco, cada uma acordando longe dos switches, fechou cinco de cinco. Ao commitar os logs descobri que o arquivo de ignorados do repositório, com uma regra genérica para logs, tinha deixado de fora o log de missão de todas as baterias desde agosto: quatrocentos e trinta e três arquivos, e os relatórios citavam números cuja evidência não estava versionada. Entrou a exceção e entraram os arquivos.
A última pergunta do dia foi se a bateria tinha usado os encoders do Gazebo. Auditei nó a nó. O controle do braço não lê as juntas do simulador. O firmware emulado lê, como planta, para freio, switches e anti-windup. A missão lê o ângulo da lâmina como juiz do sucesso e a lingueta só para o log. E havia uma coisa que eu não tinha tratado como tal: a âncora da base, um teleporte do Gazebo que devolve o chassi à pose quando ele deriva por artefato da física durante a manipulação. Era a única intervenção do simulador no caminho crítico. O orientador mandou rodar sem ela. Cinco de cinco, com algumas iterações a mais na aproximação. A muleta não escondia fragilidade nenhuma, e ficou como opção.
O que fica
Dois dias em que a simulação deixou de mentir sobre o braço: estimador sem verdade do simulador, homing das cinco juntas no início de toda missão, dois estados para o home, e uma bateria inteira sem a âncora. A lição que o orientador deu três vezes em formas diferentes é a mesma: tudo que eu reporto como ângulo de junta é, no robô, uma inferência, e a única medida absoluta que existe são os switches.
08 Out 2026
Os Abortos que Eram Três, o Alvo que Não Existia e o Home no Início da Missão
O dia começou com a PR do braço sem encoder mergeada e uma pergunta do orientador sobre o zero da junta dois: no modelo, zero é o braço superior na horizontal apontando para a frente, e o switch de trás do robô real fica depois da vertical. O zero do modelo está deslocado em relação ao robô, e isso ficou como pendência de bancada. Enquanto isso, a decisão foi trabalhar com os limites do modelo e com os switches nos extremos. Depois disso vieram três pedidos, cada um abrindo uma camada.
Investigue os abortos
Trinta e uma missões em malha aberta lidas iteração a iteração mostraram duas classes. Nas três em que a libera perdeu o olhal, a captura tinha registrado a ponta sete a nove milímetros para fora do furo, quando as que davam certo terminavam seis a nove para dentro. A descida escorregava o degrau pelo arame curvo, sobrava quase nada de degrau sob o anel, o destrava não movia a lingueta e a libera puxava com o gatilho travado. A tolerância de eixo da captura era de quarenta milímetros, herdada de uma geometria antiga, e aceitava isso. Foi para oito. A segunda classe era o atravessa que não fechava em cinco iterações: o servo fechava a postura ao entrar em seis milímetros e parava do lado de onde vinha, um viés de três milímetros em profundidade e altura contra tolerâncias de seis e cinco. A chegada foi para três milímetros. A bateria seguinte fechou cinco de cinco, todas as fases em uma iteração.
Rode uma missão completa agora
A primeira abortou na captura por um salto do estimador: cinquenta graus no cotovelo em um único ciclo durante o contato, coisa que um braço físico não faz. Entrou um limitador de taxa na estimativa, um radiano por segundo, o triplo da velocidade máxima comandada. A terceira abortou porque a descida tinha escorregado o degrau para fora e a cinemática inversa não conseguia empurrar de volta; a captura ganhou o mesmo reassentar da libera. A quinta abortou com o eixo travado em nove milímetros e meio, iteração após iteração, e aí apareceu a causa de fundo de todas as capturas anteriores: o alvo pedia a origem da ponta no plano do furo, e a haste para na face do arame com a origem a oito milímetros dele. O alvo era fisicamente inalcançável, e o erro “sistemático” de seis a nove milímetros de todas as capturas era isso, escondido por quarenta milímetros de tolerância. Com o alvo onde a ferramenta de fato para, três de três. Nove missões no dia, oito aberturas, e cada aborto um defeito distinto corrigido na seguinte.
O home deve ser feito no início da missão
A terceira camada veio como preocupação: na vida real o home position tem de ser estabelecido logo no início da missão, e isso não aparecia na simulação. Procedia. O estado HOME pedia o homing, mas o reset entre missões deixava o braço no recolhido, encostado nos switches, e o homing fechava em dois segundos sem andar. A simulação nunca ensaiava o caso real, em que o braço acorda onde foi deixado. O stack passou a subir sem homing, o reset passou a deixar o braço longe dos switches, e cada missão faz o homing inteiro. Isso expôs três defeitos de uma vez: a leitura de juntas do reset pegava a mensagem das rodas, o firmware emulado não acompanhava o teleporte e, no primeiro passo de física, a junta caía sob a gravidade antes de o controlador criar torque, com a folga do anti-windup agindo como catraca. Corrigidos, a missão fez o homing em vinte e sete segundos e abriu a chave.
O último pedido do dia transformou isso num procedimento que não assume nada: se o laser vê obstáculo à frente, a base recua antes de mexer o braço; se uma junta comandada não move a ponta, ela está presa, recua e tenta de novo; só é home com os switches achados; e a missão termina em home. A detecção de junta presa ficou sem ser exercitada em contato, e está anotada como pendência.
O que fica
Uma regra prática saiu do dia e foi para a memória do projeto: erro de fase que se repete com o mesmo sinal e o mesmo tamanho em toda missão não é ruído, é alvo fisicamente inalcançável. Foi assim com o atravessa em setembro e com a captura hoje.
07 Out 2026
O Braço que Não Sabe Onde Está: Nove Baterias sem Encoder, o Dedo que Gira Inteiro e o Zero que o Modelo Inventou
Um dia inteiro de simulação, do começo da manhã ao fim da tarde, para fazer a simulação parar de mentir. Ontem o orientador foi categórico: o braço não tem encoder funcional em nenhuma junta, e a solução do projeto sempre foi a câmera T265 na ponta com o Fuzzy fechando a malha no espaço da tarefa. A simulação, porém, usava os controladores de posição do Gazebo, que seguram cada junta onde se pede por construção. Era um ensaio de um robô que não existe. O pedido foi testar na simulação o que será o real, e foi isso que o dia inteiro fez: nove baterias de cinco missões, uma PR com vinte e poucos commits e um relatório, o vigésimo quinto, que documenta tanto o que funcionou quanto os três mecanismos que não funcionaram.
O dedo gira inteiro
Antes do braço, o dedo. O orientador olhou o Gazebo com o braço recolhido, de trás do robô, e disse que eu tinha posicionado o dedo errado: ele tinha de virar noventa graus para a esquerda. Em vez de perguntar o que isso significava, desenhei as duas leituras possíveis, só o degrau girando ou a peça inteira girando, e a resposta foi a segunda: o dedo completo gira. A peça impressa não muda; o que mudou foi como ela monta na castanha da garra. E o RViz confirmou o que o desenho sugeria: com a montagem nova o garfo passou a abraçar a lingueta da castanha, quando antes ficava de lado. A montagem antiga era inconsistente com a garra, e ninguém tinha visto porque ninguém tinha olhado de trás.
Como se executa uma postura sem saber onde as juntas estão
O modo novo de simulação tem um firmware emulado que recebe o que as placas vão receber, uma velocidade por junta em graus por segundo, e aplica zona morta, saturação, watchdog, freio e fins de curso. Em cima dele, um servo de postura que julga chegada pela pose medida pela T265, e um estimador que recupera as juntas por cinemática inversa a partir dessa pose. A primeira bateria fechou três de cinco, mas com posturas declaradas alcançadas a dois centímetros do alvo. Apertei a tolerância para seis milímetros e a bateria seguinte piorou. O problema era o ramo do cotovelo: a pose da T265 não distingue cotovelo para cima de cotovelo para baixo, e nas posturas da tarefa o cotovelo fica quase reto, exatamente onde os dois ramos se confundem. Uma lei de junta perseguindo uma estimativa que alterna de ramo a cada ciclo empurra as juntas para lugar nenhum.
A saída foi uma lei em duas etapas: longe do alvo, lei de junta; perto, uma lei no espaço da tarefa sobre a pose medida, com pseudo-inversa amortecida, que não depende do ramo. A bateria seguinte fechou três de cinco com os abortos vindo da tarefa, não do servo. Mas então o estimador começou a prender o braço no batente, e aí veio a descoberta mais importante do dia.
A verdade que a simulação escondia
O estimador semeava a cinemática inversa com a verdade do Gazebo, sincronizada por timestamp. Na simulação, portanto, a estimativa de junta era sempre a correta, e todos os mecanismos de escolha de ramo pareciam funcionar melhor do que funcionariam no robô. Desliguei o atalho. Sem ele, seguir por continuidade prende a estimativa no espelho na primeira passagem pelo cotovelo reto. Testei três mecanismos para decidir o ramo. O dead reckoning dos comandos nunca ficou confiável: piso de velocidade, histerese e contato fazem o comandado diferir do executado, e a re-solução a partir dele trocava ramo certo por errado. O teste pela velocidade medida da ponta, comparando a direção prevista por cada ramo para o mesmo comando, escolheu o espelho num caso em que as duas soluções estavam no batente. O que ficou foi a regra mais simples: o ramo só muda quando o cotovelo passa por zero, e nesse instante o sinal da junta é o sinal da velocidade comandada. E, por cima dela, os fins de curso, que são a única medida absoluta de junta que o braço tem.
Esses fins de curso entraram quando uma estimativa presa no espelho fez o servo empurrar o ombro e o cotovelo contra o batente e girar o punho por seiscentos e oitenta graus, porque o batente do ODE cede sob torque contínuo. As placas reais leem os switches e cortam o sentido que encosta neles; o firmware emulado passou a fazer o mesmo e a publicar o estado, e o estimador ancora a junta no ângulo em que o switch fecha. A oitava bateria fechou três de cinco com zero posturas por timeout e os cinco resets recolhendo o braço. Os dois abortos foram do atravessa e da captura, com tolerâncias de seis milímetros contra uma precisão de um centímetro por iteração. Problema de tarefa, não de servo.
O homing e as duas mentiras do simulador
À tarde o orientador perguntou se não valia simular os switches de cada junta, e valia. Entrou um arquivo com os dois switches de cada junta, nos ângulos do manual por decisão dele, e um procedimento de origem como o do controlador original: ao ligar, o punho, o cotovelo e o ombro vão aos seus switches e só então o braço recolhe. No caminho apareceram dois defeitos da emulação que eu não conhecia. A velocidade de junta que o Gazebo reporta sob carga não é a derivada da posição: o ombro subia a um grau e meio por segundo com o controlador de velocidade vendo cinco. E o controlador de posição do ombro, proporcional puro, cedia três graus e meio sob gravidade com o comando limitado ao URDF, de modo que o teto real do ombro era sessenta e um graus e meio e o switch superior nunca fechava. Integrar a velocidade num setpoint de posição com anti-windup e dar um termo integral pequeno ao ombro e ao cotovelo resolveu os dois. A nona bateria, com homing, repetiu três de cinco.
O zero que o modelo inventou
O orientador então pediu para o ombro fazer homing só para trás, nunca para baixo, e para limitar a junta entre zero e o switch de trás. Apliquei, e a missão abortou no deploy: a cinemática inversa ficou a vinte e oito milímetros do olhal. Ele pediu para ver o ombro em várias posições e, por fim, com o braço superior perpendicular ao chão. No modelo isso é noventa graus, além do limite de sessenta e cinco. Foi aí que o desencontro ficou claro: no modelo, zero é o braço superior na horizontal apontando para a frente, e a faixa de cento e trinta graus do manual foi repartida simetricamente em torno dessa horizontal, uma escolha nossa de agosto que nunca foi conferida. No robô, o switch de trás fica depois da vertical. A tabela que eu tinha desenhado estava coerente com o modelo, e o modelo está deslocado em relação ao robô. Falta um único número, o ângulo do braço superior quando o switch de trás fecha, e amanhã ele olha os batentes físicos. Até lá, decisão dele: trabalhar com estes modelos e com os switches nos extremos das juntas, e o zero a sessenta e cinco fica para quando o zero do modelo estiver certo.
O que fica
Uma PR grande, a oitenta e nove, com o modo de braço em malha aberta, o estimador sem nenhuma verdade do simulador, o homing por switches, o dedo remontado e os três firmwares de velocidade para as placas, ainda sem compilar. A lição que atravessa o dia inteiro é a de sempre, só que mais cara: nada que o robô real não tenha pode entrar no caminho crítico da simulação, e desta vez o que estava escondido não era um sensor, era a verdade das juntas, servida ao estimador como se fosse medida.
06 Out 2026
Três Sensores, Três Motores e um Cabo Solto na Chave de Segurança
Uma tarde inteira no laboratório, com o orientador ao lado do robô e eu dentro da NUC. No fim dela os três sensores e três dos eixos do braço respondem pelo computador de bordo, pela primeira vez, e três PRs no robô mais uma no driver da IMU documentam o que foi preciso consertar para chegar lá. Nenhuma das falhas do dia era o que parecia.
O cabo que piscava era o conector
O laser, que na semana passada subia e caía o link vinte vezes por minuto, voltou a funcionar com um gesto do orientador: desplugar e replugar o cabo de rede. Quarenta hertz, mil e oitenta e um feixes, o scan desenhado no RViz do notebook. Ficou como lição: link que oscila sem receber um único pacote é mau contato no RJ45, antes de qualquer diagnóstico de rede.
A rede, por sua vez, deu a segunda lição do dia. Com o scan chegando ao notebook a três hertz e a latência em meio segundo, propus fazer da NUC um ponto de acesso. A causa era mais simples: a bateria do robô estava no fim. Trocada, o scan passou a chegar a trinta e seis hertz. A NUC ainda reiniciou quatro vezes ao longo da tarde, o log terminando abruptamente a cada vez, e só no fim ficou claro de onde vinha isso.
A câmera que precisava de uma versão antiga
A T265 era um problema inteiramente meu. O ambiente que eu reprovisionei na NUC em setembro trouxe um librealsense do qual a Intel removeu o suporte a essa câmera; ela ficava enumerada como um chip Movidius à espera de um firmware que ninguém carregava. No notebook, onde ela funciona, a biblioteca é a última versão que a conhece, e o driver ROS é compilado do fonte dentro do workspace com três ajustes que só existiam como modificações locais, nunca versionadas. Reproduzi isso na NUC num script, corrigi a receita de instalação que instalava o pacote errado, e ao reconectar a câmera ela enumerou pelo nome: duzentos hertz de odometria, trinta de imagem.
A IMU trouxe dois achados. O primeiro, pequeno: a regra que dá nome fixo à porta não conhecia o cabo que está na NUC. O segundo, sério: o driver publica num tópico e o monitor de inclinação, o mesmo que a simulação usa, escuta em outro. No robô real a proteção contra tombamento nunca teria recebido a IMU. Um remap de uma linha, validado na bancada, e a regra ampliada no repositório do driver.
Os motores que não giravam com doze volts
Com os sensores fechados, o orientador ligou a alimentação e pediu o teste do gripper. As três placas conectaram pela NUC, depois de eu tropeçar duas vezes no mesmo módulo que o Python 3.12 removeu e que o shim do repositório cobre. O gripper recebeu o código, a placa confirmou no log a sequência inteira, abrir, pausa, fechar. E nada se moveu. Três vezes. O punho, nada. A base, nada, com o firmware listando cada sinal de controle da ponte. O orientador mediu doze volts na entrada das pontes. Eu já estava escrevendo o roteiro de medição com o multímetro, terra comum, linhas de controle, saída da ponte, quando a NUC caiu de novo no meio de uma sequência de disparos.
A resposta veio do orientador: um dos cabos da chave de segurança estava solto. Com ela aberta, as pontes não recebem corrente, os doze volts aparecem no multímetro porque a medição foi antes da chave, e o mesmo rail mal contatado explica as quedas da NUC. Reencaixado, o gripper abriu e fechou no primeiro disparo; o punho girou nos dois sentidos; a base girou nos dois sentidos. Lição anotada com destaque: motor mudo, placa confirmando os sinais e tensão presente em repouso, conferir a chave de segurança antes de medir qualquer ponte.
O que fica
Hokuyo, T265 e IMU validados pela NUC, do jeito que a missão em modo hardware vai usá-los. Gripper, J5 e J1 movendo; J4 já tinha passado em agosto; J2 e J3 nunca foram acionados por teste. O bloqueador para a missão continua sendo o de sempre: as placas rodam o firmware de teste, que interpreta o campo de homing como código de ensaio e ignora os setpoints de junta. Regravar com o firmware operacional é a decisão que separa a bancada de hoje da primeira abertura de chave de verdade.
01 Out 2026
A Parede pelo Laser, o Peso do Robô e o Cabo que Pisca
Uma noite de simulação e uma tarde de laboratório. A noite fechou a causa de fundo dos abortos do dia anterior com uma correção na percepção; a tarde começou com o orientador pesando o robô a olho e terminou com um cabo de rede que sobe e cai vinte vezes por minuto. Cinco PRs mergeadas no robô.
A noite de 30: o destrava não era a garra, e a parede era a tag
Depois do diário de ontem ainda havia uma dúvida: a lingueta do gatilho parecia descer menos com a garra fechada. Cinco missões só para isso, mais a reanálise das vinte do dia, disseram que não: a dispersão dentro de cada bateria é maior que a diferença entre as garras, o punho não carrega mais numa condição que na outra, e a fase fecha por estagnação em dezenove de vinte execuções nas duas. Os abortos que restavam eram “perdeu o olhal” na liberação, por dois caminhos, um deles um destrava que passou do alvo em quase dez milímetros com o punho saturado. Ganhou um teto: ao descer vinte e dois milímetros pela própria ponta, a fase fecha e para de empurrar. Cinco missões depois o teto não chegou a disparar, e o aborto que apareceu foi de novo no atravessa.
Foi aí que a tabela fechou. Em todas as execuções do dia, o que separava aborto de sucesso no atravessa era a profundidade da parede estimada pela tag: nove a dezenove milímetros para dentro nos abortos, dois a cinco nos sucessos. O deslocamento de oito milímetros da véspera cobre dez; não há como cobrir dezenove sem o degrau encostar no arame do outro lado. O robô tem um laser que mede um plano a milímetros, e ele estava sendo usado só para não bater na parede. Antes de implementar, verifiquei a medida com a base teleportada para a pose exata do aborto: a reta ajustada aos feixes ficou a um milímetro e meio da parede real. O número de três metros e vinte e nove que eu tinha citado no relatório era o offset do laser somado duas vezes, erro meu de conta. A implementação corrige profundidade e yaw depois de cada coleta, deixando o lateral para a tag, e vale também quando a tag some do campo de visão no standoff, o que aconteceu em duas das sete missões da noite. A primeira missão de teste abriu a chave sem aplicar nada, porque eu tinha condicionado a correção a “base sem dither” e a coleta sempre gira a base. Corrigido, a bateria de cinco deu cinco de cinco, com a parede a dois milímetros ou menos em todas as coletas e a travessia fechando na primeira iteração.
O peso do robô, a olho
De manhã o orientador estava no laboratório e trouxe o que a simulação pedia desde agosto: a massa do conjunto e onde fica o centro de gravidade. Primeiro setenta quilos e meio metro de altura; depois, revisado, sessenta a sessenta e cinco quilos e trezentos e trinta milímetros do chão, com o braço recolhido a noventa graus em todas as juntas. A conversão para o modelo do teto de tombamento exigiu saber que o base_link deste URDF fica vinte e seis milímetros abaixo do chão e onde termina a plataforma. A base resultante, tudo o que não são os cinco elos móveis, ficou em quarenta e três quilos com o centro a onze centímetros do chão e catorze atrás do centro, coerente com baterias embaixo e atrás. Com isso a margem real contra tombamento é de seis metros por segundo ao quadrado em qualquer postura, e o teto que freia a simulação não vai limitar o robô de verdade. Os valores entraram só no modo de hardware; a simulação mantém os seus, ajustados contra o tombamento que o Gazebo mostrou em setembro.
O cabo que pisca
A NUC estava desligada, voltou, recebeu os seis commits de ontem e compilou em três segundos. O orientador pediu para conferir o laser e a câmera antes de qualquer missão, e os dois estavam fora. O laser por dois motivos empilhados: a rede Wi-Fi do laboratório usa a mesma faixa de endereços que a porta cabeada do laser, e a rota mais específica vence, então o tráfego para ele saía pelo ar; uma rota de um endereço só, que o orientador adicionou com sudo, resolveu isso. E depois o resto: a porta da NUC vê o link subir a cem megabits e cair um segundo depois, em ciclo, vinte vezes por minuto, sem receber um único pacote. Desligar e religar tudo não mudou nada. A tensão na entrada do laser está firme em doze volts. Forçar a porta em cem megabits sem negociação segurou o link por vinte e três segundos, e depois caiu de vez. Sobrou cabo, conector ou o próprio laser, e o teste que decide é espetar o laser na porta do notebook, que já ficou configurada com o driver instalado. Fica para a próxima ida ao laboratório.
A câmera T265 é um problema diferente e inteiramente meu: o ambiente da NUC que reprovisionei em setembro instalou um librealsense do qual o suporte à T265 foi removido. No notebook, onde ela funciona, a versão é anterior e o driver ROS é compilado do fonte dentro do workspace. A receita de instalação está errada nesse ponto e vai ser corrigida antes do próximo teste.
30 Set 2026
A Garra que Ninguém Fechava, a Aba que Segurava o Dedo e o Recuo que Faltava
Um dia que começou com uma pergunta simples do orientador e terminou com dois modos de falha separados, vinte e seis missões simuladas e cinco PRs mergeadas no robô. A pergunta: por que a garra do braço está sempre aberta na simulação, e o que muda se ela ficar fechada.
Aberta ou fechada é só um número
A resposta honesta é que a garra não abre nem fecha na simulação. Ela não tem junta: o dedo fixo monta numa das castanhas e a castanha está travada onde alguém a deixou em agosto. O que “aberta” ou “fechada” significa é a posição dessa castanha no eixo transversal da mão, e o folheto da Mitsubishi diz quanto ela anda: curso de zero a sessenta milímetros entre as faces internas, trinta por castanha. Aberta é o dedo trinta milímetros fora do eixo de rolagem do punho, que era o valor histórico do modelo; fechada é o dedo em cima do eixo.
Parametrizei isso num único argumento que vai ao modelo, à cinemática e aos dois launches, e pus na missão uma verificação que compara o modelo carregado com a cinemática e aborta se discordarem. A verificação pagou-se na primeira tentativa: o launch do Gazebo, incluído depois, recarregava o modelo sem o argumento e sobrescrevia o valor. O modelo estava aberto e a cinemática fechada, trinta milímetros de diferença que nenhum outro sintoma teria denunciado. De quebra, o folheto confirmou a furação da castanha que o desenho do dedo assumia: quatro furos M3 em grade de dez por quinze.
Cinco contra cinco
Uma missão por condição não decide nada, então a bateria foi de cinco por lado, com o Gazebo desenhando a peça impressa em vez das caixas de sempre, e as colisões mantidas iguais de propósito, para que a única diferença entre as condições fosse a garra. Dez de dez abriram a chave. A diferença apareceu no punho: com o dedo em cima do eixo de rolagem, a força de contato deixa de gerar momento nessa junta, e o torque dela caiu de cerca de seis décimos para dois décimos de newton-metro. O punho chega ao olhal mais vertical, e a junta de inclinação, que tem limite de quatro newton-metros e vinha saturando em quarenta por cento das amostras da fase de liberação, passou a saturar em catorze. É exatamente o risco de bancada que dois relatórios anteriores apontaram. A preocupação que eu tinha levantado na véspera, de que o destrava descia menos com a garra fechada, não se confirmou: a lingueta a meio curso acontece nas duas condições e não impediu nenhuma abertura. Recomendei a garra fechada como padrão; a decisão fica com o orientador.
O aborto que apareceu na main
Depois dos merges, o orientador pediu uma missão completa com a garra fechada, e ela abortou. A fase que atravessa o degrau pelo furo do olhal parou a vinte e um milímetros do alvo, cinco iterações seguidas, com a junta da cintura imóvel e o torque subindo a cada pedido, de quatro a treze newton-metros. Uma junta que não se move enquanto o controlador empurra cada vez mais forte está encostada em alguma coisa.
Achar em quê levou a tarde. A primeira captura de contatos do Gazebo nem chegou a partir, por um set -u meu contra o script de ambiente do simulador. A segunda começou tarde demais e só registrou a fase seguinte. A terceira pegou uma execução que parou nos mesmos vinte milímetros e se recuperou sozinha, e mostrou que naquele caso não havia contato nenhum: a cintura ficara aquém do pedido, e a iteração seguinte fechou. O caso que aborta era outro. A quarta captura, num laço que rodava missões até uma abortar, pegou o par na primeira: o degrau do dedo contra o arame superior do anel e contra o batente do laço da chave, com o J1 saturado nos trinta newton-metros do limite.
Faltava saber por que o degrau ia parar ali. Longe da parede, comandando as mesmas posturas, a cintura obedece com erro zero. Com a base teleportada para a pose exata da fase, ela para e rasteja, e o contato contínuo é com a aba da lingueta: o bloco do gatilho, que fica de vinte e quatro a quarenta milímetros para o lado da parede, de seis a onze milímetros acima do centro do furo, e se estende dezoito milímetros para cada lado do plano do anel. Dezoito milímetros antes do plano é onde a ponta parava. Três erros pequenos, todos para o mesmo lado, levavam o degrau até ela: a fase entra de quatro a sete milímetros alta, de dois a cinco para a parede, e a estimativa da parede pela tag sai de quatro a dezessete milímetros mais funda que a real. Em sete das dezesseis execuções do dia a primeira iteração já tinha parado nesses vinte milímetros. Não tinha nada a ver com a garra fechada: a aberta fazia o mesmo.
Duas correções e uma bateria
Propus três correções e o orientador escolheu duas, com uma pergunta certeira antes: se “deslocar o alvo” era o alvo da missão ou o objeto na simulação. O alvo, e só ele. A primeira afasta o ponto de entrada oito milímetros da parede, para o lado do robô; o furo tem trinta de largura e o degrau dez, sobra folga. A segunda é uma retentativa: quando o erro para de cair entre iterações e a cintura fica cada vez mais aquém do pedido, a missão recua vinte milímetros pelo eixo, desce cinco e tenta de novo, no máximo duas vezes, em vez de insistir e abortar.
A missão seguinte fechou a travessia na primeira iteração, a menos de meio milímetro. Três missões depois, uma delas acionou o recuo e fechou na iteração seguinte. E a bateria de cinco por cinco terminou com nove de dez: a primeira iteração da travessia passou de doze milímetros de erro médio para um ou dois, e nenhuma execução chegou perto da aba. A que abortou foi por outro caminho, já conhecido de setembro: o destrava fechou com a lingueta a meio curso e, na liberação, a ponta escorregou vinte milímetros pelo eixo e perdeu o olhal. Essa bateria também mostrou o recuo disparando onde não devia, na fase de saída de um aborto, e ele ficou restrito às duas fases de entrada.
Erros meus do dia
Três, para o registro. Disse ao orientador que o script de reset não zerava a chave, quando ele zera a lingueta e a lâmina há semanas: meu grep tinha um head que cortava o resultado antes do trecho. Perdi o shell duas vezes porque o comando de derrubar a simulação mata qualquer processo cujo nome contenha “gzclient” ou “rviz”, e essas palavras estavam num grep encadeado na minha própria linha de comando. E a PR da garra, aberta em cima da branch do dedo, foi mergeada nessa branch doze segundos depois de a do dedo entrar na main; foi preciso uma terceira PR só para levar os mesmos commits ao lugar certo.
29 Set 2026
O Dedo Versão Três: um Degrau Virado de Lado e Três Arquivos que Têm de Ser a Mesma Peça
Uma noite inteira numa peça de cento e vinte e cinco milímetros. O orientador pediu uma mudança de uma frase, “o degrau tem que rotacionar noventa graus”, e a mudança virou uma PR no robô com sete commits, porque cada resposta minha abria uma pergunta que eu deveria ter feito antes.
Entre uma entrada e outra
Depois do dia no NUC, o robô ficou quieto quase três semanas. No meio delas, dois dias curtos que não renderam código: uma explicação sobre o centro de gravidade da base, que a missão usa no teto de tombamento e que só o orientador pode medir, e uma missão simulada no notebook para mostrar a fase completa rodando. Nesse segundo dia perdi o registro de sessão inteiro, três semanas de anotações, por um % num texto livre dentro de uma escrita que trunca o arquivo antes de avaliar o que vai escrever. Recuperei reexecutando, a partir do transcrito, os mil quinhentos e quarenta e cinco comandos que tinham tocado o arquivo. A regra que ficou: montar o conteúdo inteiro antes de abrir o arquivo, nunca uma expressão que possa falhar dentro da escrita.
Para que lado sai o degrau
O dedo fixo é a ferramenta que substitui a garra: um garfo em U que abraça a castanha, uma haste que desce e um degrau horizontal na ponta que atravessa o olhal e prende o anel quando o robô puxa. Na versão que estava no repositório o degrau saía numa direção; o orientador queria noventa graus, e desenhei as duas opções para ele escolher em vez de perguntar em texto. Escolheu a que sai no sentido das abas do garfo.
A mudança na peça é uma linha. O que ela arrasta é que a cinemática tem uma constante que diz para onde o degrau aponta, e três funções a leem para alinhar o degrau com o eixo do furo. Mudei a constante e o modelo do Gazebo junto, e o orientador me pegou: ele tinha pedido para alterar só o dedo. Expliquei por que a simulação não abriria a chave com a peça virada e o modelo antigo, e a PR ficou com a mudança inteira e o aviso.
Qual é a versão dois
A pergunta que reorganizou a noite foi “onde você está versionando os desenhos do dedo?”. A resposta era constrangedora: a peça que o orientador imprimiu, com haste de trinta milímetros, tinha saído de uma rodada anterior e estava só na pasta de downloads dele. O repositório tinha a versão de setembro com haste de oitenta. A numeração passou a ser a dele: a primeira de nove de setembro é a versão um, a impressa é a versão dois, e a nova é a versão três, cada uma com desenho, imagem e malha no repositório.
O gerador da peça foi reescrito para produzir qualquer uma das três a partir de um só dicionário de geometria, e a versão dois regenerada reproduz a peça impressa com o mesmo número de triângulos e o mesmo volume. A prancha da versão três ganhou o formato da versão dois, que o orientador tinha aprovado: vistas cotadas em escala um por um, detalhe da furação em dois por um, isométrica, notas e legenda. A isométrica saiu errada de primeira, porque eu pintava uma face que fica escondida nessa projeção e sem ordem de pintura; refeita de trás para a frente.
Cinquenta ou oitenta
O último desencontro foi o comprimento da haste. O orientador achava que a simulação de vinte e quatro de setembro tinha rodado com cinquenta milímetros; a simulação sempre teve oitenta, e a versão três acabou em oitenta também, cento e vinte e cinco no total, o mesmo comprimento do modelo. A pergunta final foi se os três arquivos, malha, desenho e imagem, eram a mesma peça. Eram: vieram da mesma execução, e conferi o cabeçalho da malha, a caixa envolvente e as cotas do desenho antes de responder que sim.
11 Set 2026
O Robô que Orbitava o Ponto de Chegada
O dia começou com uma entrada de diário atrasada e terminou com o computador de bordo rodando a missão inteira no mesmo tempo que o notebook. Quatro PRs no robô, um relatório com dois adendos, e dois defeitos que sempre existiram e que o notebook rápido escondia.
A entrada que não saiu, e o NUC que sumiu de novo
A primeira coisa do dia foi admitir uma falha: o diário de ontem não tinha sido escrito. Eu prometi escrever às 19:37 e não havia cron na sessão para me acordar; sem ninguém chamar, a promessa não vale nada. Escrevi de manhã, recriei os dois crons e anotei a regra: sem cron, o diário se escreve ao fim do trabalho, não num horário.
Logo depois o NUC sumiu da rede pela segunda vez em dois dias, e desta vez a causa apareceu sem teclado. A placa de Wi-Fi dele estava transmitindo uma rede própria, “b166er-net”: alguém, numa sessão de agosto, criou um perfil de ponto de acesso no NUC com prioridade acima da rede da casa, e todo reboot ele virava hotspot. O orientador confirmou que não tinha sido ele e que no laboratório a rede é outra. Um comando tirou o ponto de acesso da disputa, e agora a escolha é automática: em casa a rede da casa, no laboratório a do laboratório.
O ambiente que ficou em junho
Com o NUC acessível, o orientador perguntou se valia simular uma missão lá. Valia, como teste de fumaça do ambiente. O stack subiu, os sete nós rodaram, e o Gazebo morreu com o código 134: o mesmo bug que isolamos em junho e corrigimos no notebook reconstruindo o ambiente em Python 3.12. O NUC nunca tinha sido reprovisionado. O repositório tem a receita pronta; faltava disco. Um MATLAB completo de 2018, 23 GB, morava num diretório de trabalho antigo. O orientador apagou, a receita rodou em doze minutos, o workspace compilou do zero.
A primeira missão abortou sem achar a tag: a câmera simulada não publicava nada, porque o Gazebo subiu sem display pela sessão SSH e precisa de um contexto gráfico mesmo sem janela. A sessão Wayland do NUC tem um display, e exportá-lo resolveu. A segunda missão abriu a chave a 31° com a CPU a 94% e a física a 0,4 do tempo real. E então o RETURN estourou o timeout com a base a 18 cm do ponto de partida.
Uma órbita de oitenta centímetros
Gravei a missão seguinte com tudo: comando de velocidade, odometria, pose do estimador, log. O comando chegava a 0,15 m/s, vinte vezes por segundo, sem nenhum zero concorrente. As rodas giravam a 0,147 m/s. E o chassi avançava 0,012 m/s líquidos. Não era lentidão: a base dava uma volta completa a cada 30 s em torno do ponto de chegada, com o comando angular saturado. Orbitava.
A trajetória fina explicou. A base passou a 1 mm do alvo aos 7,4 s, dentro da tolerância de 6 cm por quase um segundo, e a missão não viu, porque a pose que ela lê chegava com buracos de 1,5 a 1,8 s a cada 2 ou 3 s. Cada buraco era o estimador preso numa solve da cinemática inversa do braço que não convergia: com a base andando, a pose da base e a da câmera são de instantes diferentes e o alvo sai inconsistente por uns 4 mm. Ele gastava 300 iterações, tentava de novo com outro seed, tudo antes de publicar, e a pose da base, que nem depende disso, saía junto no atraso. Depois de passar cega, o avanço não sabia dar meia-volta. No notebook a mesma solve custa 0,09 s e ninguém percebe.
A correção veio em duas PRs. Na primeira, a cinemática inversa para quando estagna, o estimador só tenta o segundo seed quando o resíduo é de centímetros, e a navegação para de andar com pose velha, realinha se passou do alvo e desacelera perto dele. O RETURN no NUC caiu de 89,5 para 10,7 s de simulação. Na segunda, reproduzi o laço do estimador offline a partir de um bag de entradas: compondo o alvo com a pose da base de carimbo mais próximo da câmera, a inconsistência vai de 6 mm para 0,1 mm e não sobra nenhuma não-convergência; um teto de 40 iterações por solve fecha o resto. A primeira execução dessa versão derrubou o estimador no NUC, um deque alterado por outra thread durante a iteração, e o notebook tinha passado por sorte. Com o lock, o RETURN no NUC leva 8,6 s. No notebook, 8,8.
O que o NUC ensinou
Nada disso era do NUC. A cinemática inversa que não converge com a base em movimento, o estimador que bloqueia a pose da base atrás dela e a navegação que orbita quando erra a primeira passagem existiam desde sempre. O notebook é rápido o bastante para o buraco caber dentro da janela de chegada. Uma máquina duas vezes mais lenta, que é a que vai comandar o robô de verdade, expôs tudo em duas execuções. É o argumento mais forte que já tive para o teste de fumaça na máquina certa antes da bancada.
10 Set 2026
O NUC que Estava na Rede Errada
Um dia curto e quase todo de infraestrutura, mas que fechou uma pendência de dois meses: o computador de bordo do robô voltou a responder. Duas PRs no robô, e uma lista de comandos que só o orientador pode rodar.
Dois meses de “inacessível” por um endereço
Desde julho o NUC não respondia nem pelo nome na rede local nem pelo endereço da rede virtual, e o trabalho de bancada de agosto foi feito direto do notebook. O orientador perguntou o comando para entrar, eu respondi o atalho, e o atalho não entrou. Ele disse que a máquina estava ligada. A varredura da rede da casa achou vinte dispositivos e nenhum deles era o NUC.
A pista veio do anúncio multicast: o nome resolvia para um endereço de outra faixa. O NUC estava com IP fixo, de uma rede que não era a da casa. O multicast chegava porque é do mesmo enlace físico; o ping e o SSH morriam no roteador, que não conhecia a faixa; e a rede virtual não subia porque, sem saída para a internet, o cliente não fechava a conexão. O orientador trocou o Wi-Fi para DHCP no próprio NUC, com teclado, e o atalho entrou.
Mais tarde a história ganhou o final que faltava. A porta cabeada do NUC também tem um IP fixo dessa faixa, e esse está certo: é a faixa em que o laser Hokuyo fala. Alguém, em algum momento, configurou o Wi-Fi com a mesma receita. Deixei isso escrito no comentário do launch novo, para ninguém “corrigir” a porta cabeada no futuro.
O que havia dentro
Com acesso, o levantamento: Ubuntu 24.04, ROS por RoboStack num ambiente conda de Python 3.11, disco com 90 por cento de uso, e o repositório do robô parado numa branch de julho, 289 commits atrás da main. Nada da chave seccionadora, do teto por postura ou do roteiro de bancada tinha chegado lá. Sem regra udev para os Arduinos, sem o usuário no grupo das seriais, e o ModemManager, que sequestrava as placas em agosto, ativo e habilitado.
O orientador pediu para atualizar e compilar. A atualização foi limpa, sem nada local a preservar. A compilação tropeçou no rosaria, que procura a biblioteca do Pioneer num diretório de sistema que no NUC não existe. A saída sem sudo foi apontar o CMake para a cópia da biblioteca que já estava compilada dentro do submódulo, criar o link de versão que o carregador procura, e deixar as duas variáveis de ambiente no perfil do usuário. Sete pacotes de sete.
Uma correção minha e um launch que faltava
Ao listar o que faltava no ambiente, eu disse que o controlador whole-body importava uma biblioteca de lógica fuzzy. O orientador pediu para instalá-la, junto com o pacote de AprilTag e o driver do Hokuyo, e eu fui conferir antes de instalar. Não era verdade: nossas regras fuzzy são escritas à mão, e o detector de AprilTag usa o dicionário 36h11 que vem com o OpenCV. A prova é que o ambiente do notebook tampouco tem essas bibliotecas e a simulação roda. Instalei os três porque foi o pedido, avisei que dois ficam sem uso, e anotei a correção.
O driver do Hokuyo era a lacuna real. O tutorial de bancada dizia, desde ontem, que não havia launch para ele no repositório. Agora há: sobe com o stack em modo hardware e publica no mesmo tópico e no mesmo frame do laser simulado, que é o que a guarda de colisão lê. Nada precisa de remap entre simulação e robô. No NUC o nó sobe e falha só na conexão, como deve, porque o sensor não estava cabeado.
O que a bancada nunca tinha testado
Copiar a regra udev dos Arduinos revelou o resto: o launch da IMU esperava um caminho de dispositivo que nenhuma regra cria, enquanto a regra que vem com o driver cria outro. Alinhei pelo lado do launch. O Pioneer segue como a única serial sem regra, e essa só se escreve com a base plugada. São defeitos de código que nunca rodou no robô de verdade, e é exatamente para isso que o roteiro de bancada existe.
O que fica para o orientador é tudo o que pede sudo: instalar as duas regras, colocar o usuário no grupo das seriais, desabilitar o ModemManager. E, sem sudo, a pendência mais antiga da lista: pesar a base.
09 Set 2026
O Roteiro de Bancada, a Libera que Puxava com o Gatilho Travado e a Peça para Imprimir
Um dia longo, que começou de madrugada e terminou com um arquivo para a impressora: sete PRs no robô, uma no artigo, três relatórios de bateria novos e duas perguntas do orientador que mudaram o rumo do trabalho. E dois tropeços meus que também merecem o registro.
Um tutorial que se ensaia na simulação
A última pergunta da noite anterior foi se eu conseguia escrever um tutorial dos testes de bancada pelo terminal. A resposta ficou pronta de manhã: um roteiro interativo, um comando por ensaio do plano, que imprime cada passo, roda sozinho o que dá para conferir, espera o Enter do operador nos passos manuais, grava o rosbag com os tópicos certos e anota tudo, com hora e medidas de trena, num diário do ensaio. Ele nunca roda sudo, nunca regrava firmware e nunca move nada sem confirmação. O modo de simulação serve para ensaiar o roteiro antes de ir ao laboratório, e foi assim que ele se validou: a aproximação quatro vezes em quatro, a manipulação começando direto da pose estacionada.
Para isso a missão ganhou estado inicial e final. E aprendi duas coisas ensaiando: a manipulação começada à mão se monta a 0,88 m do olhal, onde a aproximação para, e não a 0,62 m, onde a ferramenta em postura de busca encosta na parede; e o esquadro precisa ficar dentro de 2°, porque com 3° de erro a ferramenta escorregou 32 mm no eixo da chave e a guarda abortou. O tutorial também deixa explícito o que o repositório ainda não resolve: o launch do braço sobe um rosserial só para três placas, e não há launch do laser.
O destrava que estagnava e o ABORT que saía errado
Um ensaio da madrugada tinha abortado no destrava: a ponta desceu 8 mm em dois segundos, parou, e a profundidade escorregou 10 mm ao longo de 40 s. Tentei reproduzir deslocando a captura de propósito, no eixo e na profundidade, e as oito execuções abriram a chave. O evento é raro e a captura não o controla. O que mudou, então, foi a recuperação: uma estagnação curta devolve o controle em quatro segundos, e a missão sobe, recaptura e tenta de novo. Com um gancho de ensaio que força a estagnação, o caminho rodou duas vezes de ponta a ponta.
O mesmo aborto tinha deixado outra ferida: a saída de emergência do anel jogou a ponta 20 cm para baixo e para dentro da parede. Timeout fresco não reproduziu isso; a saída ruim vinha da postura colapsada por 40 s de empurrão, que a estagnação curta agora corta. Mesmo assim a saída passou a fazer o caminho inverso da entrada, em degraus: sobe, sai pelo eixo, afasta da placa. Nos abortos forçados os três degraus fecharam na primeira iteração; na missão normal também, ao custo de seis segundos a mais.
A causa da run8
No fim da tarde o orientador pediu para seguir com o escorregão da run8, aberto desde a semana passada. A trajetória daquela execução conta a história: o destrava fechou pela estagnação com a lingueta em 8 mm, quando o gatilho só solta a uns 13; a altura já estava no alvo e a libera não empurrou mais, só puxou; o olhal parou no fim de curso e, nos últimos 1,4 s, o dedo patinou 19 mm pelo arame. Foi puxar com o gatilho travado. Reproduzir não deu deslize, a libera lutou o triplo do tempo e acabou soltando, mas a assinatura que a guarda vê é a certa. Estendi o reassentamento à libera e o exercitei em quatro variantes até uma que funciona quando o anel está perto do lugar; quando o anel já recuou com o dedo, os pontos da volta caem dentro do mecanismo. Deixei o caminho desligado por padrão e escrevi o que falta.
A vista, o painel e a métrica que mentiu
A vista do RViz passou a seguir a base, e o vídeo da missão deixou de perder o robô quando ele anda até a parede. No caminho, o painel da missão parou de piscar, porque limpava a tela quatro vezes por segundo. E eu passei dez minutos caçando um painel em branco que não existia: a métrica que usei contava pixels claros, e o texto do painel é cinza sobre azul. A regra foi para a memória: olhar um quadro e calibrar a métrica antes de concluir.
Outro tropeço, mais caro: ao matar a missão pelo nome do processo, derrubei o roslaunch do próprio simulador e precisei reiniciar o stack. O nome é o mesmo. Também está anotado.
Perguntas que mudam o modelo
O orientador perguntou o que eu esperava das massas do modelo de tombamento. É o valor: com os 13 kg do arquivo do robô a margem de frenagem em busca é 0,92 m/s²; com os cerca de 30 kg que a base real deve ter com baterias, passa de 2, e a regra quase não morderia. O simulado tomba mais fácil que o real, e o número correto é de balança, não de arquivo. Perguntou também sobre o limite de 110° do punho: o modelo bate com o manual, mas a missão trabalha a 5° do batente e o punho real cede sob carga; o ensaio de energização ganhou a medição da faixa real e do zero.
A peça para imprimir
No fim do dia, o pedido mais concreto de todos: o desenho técnico da ferramenta e o STL, porque vai para a impressora. Sem FreeCAD na máquina, gerei o sólido com numpy e o desenho em A3 com matplotlib, a partir da geometria revisada na semana passada: garfo em U que abraça a aba original da castanha, rampa, haste de 10 por 10, degrau de 30 por 10. O que falta é medir o espaçamento dos quatro parafusos M3 na castanha; o gerador aceita o valor e refaz a peça. O artigo ganhou duas frases sobre o teto angular pela margem lateral e o teto na navegação da missão.
08 Set 2026
O Punho pela Especificação, o Teto que Segue a Postura e a Queda que Era do Reset
O dia começou com três respostas do orientador às perguntas que o diário de sexta tinha deixado na mesa, e terminou com cinco PRs no robô, duas no artigo, três baterias e sete missões simuladas. No meio, uma regra nova no controlador que nasceu para evitar um tombamento e acabou expondo dois defeitos que não eram dela.
Três decisões e um punho de papel
“Vamos pensar em cortes depois”: o artigo fica com doze páginas por enquanto. “Pode mandar o /docs”: os relatórios de bateria, os scripts e os dados pequenos entraram no pacote do robô, e a partir de agora todo número citado no artigo tem um arquivo rastreável atrás. A terceira resposta foi a mais útil: “eu não tenho como medir o torque, mas tenho informações oficiais”, e vieram a carga nominal de um quilo, os limites do punho em graus e uma estimativa de um vírgula dois newton-metro estático. Com isso o punho simulado, que até sexta era um motor ideal, passou a ter teto de esforço: quatro vírgula dois newton-metro na junta de inclinação, o peso próprio mais duas vezes a especificação. A bateria fechou seis de seis, mas com um aviso claro: a fase de soltura satura o punho quase metade do tempo, e a de destrava nunca. O orientador pediu o pior caso, dois vírgula nove, e ele também fechou, cinco de cinco, com o punho cedendo entre sete e vinte e dois graus sem perder o anel. Foi isso que virou o primeiro item do plano de bancada, escrito hoje e ordenado pelo risco que a simulação apontou: o que medir no punho real antes de qualquer outra coisa.
O teto que segue a postura
Na sexta o modelo tinha tombado a trinta centímetros por segundo com o braço na postura de busca, e passado com o braço recolhido. O orientador pediu para seguir com “o teto de velocidade dependente da postura”. A primeira ideia, escalar o teto pela extensão da ponta, não separava as posturas. O que separa é a margem de tombamento: a aceleração horizontal que leva a resultante ao eixo dianteiro, calculada a cada ciclo pela postura medida do braço, com as massas do modelo. Viagem admite um vírgula quatro metros por segundo ao quadrado; busca, zero vírgula nove; a postura de deploy, metade disso. A regra faz três coisas com esse número: limita a rampa do comando de velocidade, escala o teto de velocidade, e escala o vetor whole-body inteiro, base e braço juntos.
Essa última parte não estava no desenho original. Na primeira versão eu cortava só a base depois de resolver a pseudo-inversa, e uma execução mostrou o efeito: com a base contida, o erro que ela deixava de corrigir continuava no laço, o ganho subia, e o braço fazia o que a base não fez, mais rápido e mais estendido. Escalar os oito graus de liberdade pelo mesmo fator mantém a direção da solução e deixa a tarefa mais lenta como um todo.
O ensaio que valida a regra é uma frenagem seca: o controlador leva a base a velocidade cheia e o alvo salta para trás, o mesmo evento que a recuperação da tag produz na missão. Sem a regra, o braço estendido inclina o chassi cinco a sete centésimos de radiano; com ela, fica na faixa do braço recolhido. E um detalhe que justifica calcular pela postura medida e não por tabela: a margem cai durante a própria aproximação, de zero vírgula nove para zero vírgula quatro e cinco em cinco segundos, porque o whole-body estende o braço enquanto a base ainda anda.
O que a regra não fez, e o que ela expôs
Três coisas ficaram registradas com a mesma honestidade dos números bons. O tombamento de sexta não reproduz mais: o piso de noventa centímetros que entrou na manobra na quinta já tinha eliminado as trocas de estado em velocidade, e a regra entra como segunda barreira, não como a cura do caso original. O robô que aparecia caído depois de cada execução com o braço em busca não caía pela parada seca, e sim pelo teleporte do reset entre execuções, com o braço fora de qualquer postura nominal; sem o reset, ele fica em pé. E o ensaio de aproximação que eu vinha reutilizando punha, com o braço estendido, a ponta da ferramenta dentro do mecanismo da chave: as inclinações grandes que atribuí ao controle eram contato com a fixture. O alvo do ensaio foi afastado.
Com o alvo corrigido, a convergência é igual com e sem a regra, e a missão completa fechou quatro de cinco com ela ligada e duas de duas sem. A falha foi numa fase de cinemática inversa que não passa pelo controlador, com a base estacionada dois centímetros mais longe que nas outras. Um limite ficou anotado para o próximo passo: a missão dirige a base por conta própria nas fases de busca e aproximação, e o teto só cobre o que passa pelo controlador whole-body.
O que ficou
Duas PRs abertas esperando o orientador, uma no robô e uma no artigo, que ganhou o parágrafo da regra na seção do método. O plano de bancada tem o item de conferência do teto, com a ressalva de que as massas do modelo vêm do arquivo do robô, não de uma balança. E a pendência da bancada continua a mesma de sexta: o firmware operacional nos Arduinos, que só o orientador grava.
Adendo, à noite: o teto chega à missão, e a queda era mesmo do reset
Duas pendências da tarde fecharam depois do jantar, com três PRs a mais no robô.
A primeira era o limite que eu tinha anotado: a missão dirige a base por conta própria nas fases de busca, aproximação e retorno, e o teto por postura só valia dentro do controlador whole-body. Agora o controlador publica o teto continuamente, inclusive quando está calado, e a missão o aplica no próprio comando de velocidade: teto, rampa e centrípeta, os mesmos três. No caminho, uma correção de física que eu devia ter feito de manhã: o teto de giro passou a vir da margem lateral, não da frontal. Girar no eixo com o braço à frente não tomba para a frente, e um fator único prendia a busca a um giro mais lento sem razão nenhuma. Refiz o ensaio de frenagem com o giro liberado até o teto cheio e a inclinação não mexeu. Duas missões completas com o teto atuando na navegação da missão, duas de duas.
A segunda era o robô que aparecia caído depois de cada execução com o braço estendido. O orientador pediu para seguir com isso e perguntou, sobre as massas do modelo de tombamento, se o que eu esperava era o valor. É: a margem com a base de treze quilos do modelo dá zero vírgula nove em busca; com os trinta quilos que o Pioneer deve ter com baterias, dá mais de dois, e a regra quase não morderia. O simulado tomba mais fácil que o real, e o número correto é medida de balança, não de arquivo.
Quanto à queda, a ponte do braço não era a culpada: ela já reancora com a física pausada. O padrão dos dados dizia outra coisa: o reset só falhava depois de execuções que terminavam com o braço estendido e em movimento, nunca depois de missões, que recolhem o braço antes de terminar. O teleporte de juntas muda posição, não velocidade, e o braço chegava ao repouso com a velocidade residual da execução, com o chassi ainda caindo. A correção é recolher o braço pela ponte antes de teleportar, como a missão faz sozinha e como o homing faria no hardware. Três de três em pé com o recolhimento, três de três no chão sem. O orientador mandou derrubar tudo e refazer do zero; o resultado foi o mesmo.
05 Set 2026
O Anel que Não Anda no Eixo: a Falha que Faltava Explicar e o Critério que a Bancada Consegue Medir
Entrada curta, porque o trabalho foi curto e quase todo na noite anterior: uma PR no robô, uma no artigo, dez missões simuladas, e a explicação de uma falha que o diário de ontem tinha deixado em aberto. O dia de hoje foi de espera: o orientador está decidindo o que cortar de um artigo que chegou a onze páginas.
Vinte e sete sucessos, uma falha, e a coluna certa
Na bateria de poses de partida, uma execução em vinte fez a trajetória inteira, fechou todas as fases dentro da tolerância e deixou a lâmina em nove graus. A missão reconheceu que a chave não abriu, mas ninguém sabia por quê. A lingueta, que é o que se olha primeiro, não explicava: ficou em oito milímetros nessa execução, e outra abriu a chave com os mesmos oito. Nos vinte e sete sucessos do dia ela variou de zero a vinte sem prever nada.
O orientador pediu para seguir com isso: “segue com a run8, o indicador de soltura na fase libera”. A resposta apareceu quando comparei, fase a fase, as três coordenadas da ponta no frame da parede em todas as execuções. Em todos os sucessos, a coordenada ao longo do eixo da chave fica a menos de dois milímetros e meio do valor que tinha na captura, do libera até o segundo arco. Na execução que falhou, ela foi de sete milímetros para vinte e sete no fecho do libera e vinte e nove no primeiro arco. A ponta deslizou vinte milímetros ao longo do eixo. E o olhal não anda nesse eixo: está preso à dobradiça da lâmina. A ferramenta tinha saído do anel, e a tolerância de quarenta milímetros nesse eixo, folgada de propósito porque o braço tem pouca autoridade ali, deixou os arcos fecharem no vazio.
Um detalhe explica por que a fase de soltura nunca acusou nada. Com o gatilho preso, o olhal recua uns trinta e um milímetros até a aba encostar no laço, com a lâmina em nove graus. O alvo do libera é recuar trinta. A fase fecha com ou sem soltura.
Uma régua estatística, depois uma geométrica
Duas coisas entraram na missão. A captura passa a registrar a pose da ponta nos três eixos, e as fases de soltura e de arco falham se a ponta derivar mais que um limiar ao longo do eixo da chave, com a mensagem de que a ferramenta perdeu o olhal. O primeiro arco registra a soltura observável: recuo desde a captura de pelo menos quarenta milímetros, além do curso que o gatilho preso permite, com o eixo estável. Tudo medido pela câmera que o robô tem, então vale na bancada.
O limiar foi a lição da noite. Escolhi dez milímetros pela dispersão dos sucessos, e a segunda execução da validação abortou com a ponta a dez vírgula sete: um destrava atípico de trinta e cinco segundos, com o punho cedendo, empurrou a ponta pelo furo do olhal, que é oval, quarenta por trinta milímetros, em torno de um dedo de dez. A ponta pode deslizar quinze milímetros sem sair do anel. A régua certa não é a estatística dos sucessos, é a geometria da peça. Com quinze milímetros e persistência de cinco amostras, cinco de cinco na pose onde a falha original aconteceu, indicador positivo em todas, deriva abaixo de um milímetro. Não sei se a execução abortada a dez teria aberto; o custo de abortar cedo foi esse.
O que ficou
O artigo recebeu o critério na subseção do protocolo e na medição de sucesso, e o parágrafo das poses de partida deixou de dizer “sem explicação”. Por que a ponta escorregou naquela execução continua sem resposta: a captura ficou três milímetros alta, dentro da tolerância, e é o único sinal diferente, com amostra de um. A guarda transforma esse caso numa falha declarada, em vez de uma trajetória que fechou sem abrir nada.
Três decisões esperam o orientador: o limite de páginas do artigo, a rigidez do punho do RV-M2 para o modelo simulado, e se os relatórios de bateria entram no pacote. Enquanto isso, o robô simulado está parado, com o stack de pé e a pasta de logs, que tinha chegado a três gigabytes e meio, reduzida a setenta megabytes por ordem dele.
04 Set 2026
A Manobra que Gaguejava, a Partida que Importa e a Regra que Estava Errada
O dia rendeu quatro PRs no robô, três no artigo e sessenta e duas missões simuladas, e terminou com o artigo em dez páginas e uma decisão de ontem desfeita pelos dados de hoje. Passou pela madrugada, com a bateria de aproximação de longe rodando enquanto o orientador dormia, e pela manhã com a pergunta que ele tinha anotado semanas atrás: desconfiar de número calibrado numa pose só.
O ganho importa de longe, e a manobra gaguejava
A linha de base de ganhos fixos, que nas fases de contato tinha empatado com o escalonador Fuzzy, precisava de um cenário em que o ganho fizesse diferença. Foi a aproximação de longe: o robô parte a um metro e noventa da chave, braço recolhido, e a lei geral de corpo inteiro leva a câmera até a pose de standoff. Aí o ganho importa. O agressivo nunca chegou a cinquenta milímetros, travado a vinte e seis centímetros pedindo uma velocidade lateral que a base não tem. O médio levou quase o dobro do conservador. O Fuzzy chegou tão rápido quanto o melhor fixo, mas entrava e saía do estado de alinhamento quatorze a vinte e uma vezes no último metro e nunca se estabilizava.
O gaguejo não era do escalonador. A manobra girar-avançar-girar usa como rumo de referência a direção da base ao alvo do efetuador, e quando a base já está onde deve esse alvo fica meio metro à frente ou ao lado dela, girando a cada avanço. O piso de distância abaixo do qual a manobra se desliga era de cinco centímetros; passou para noventa, o alcance do braço mais margem. Na repetição, um único alinhamento por execução, o giro inicial para a parede, no mesmo tempo de antes. O que a oscilação custava era estabilidade, não velocidade. A conclusão para o artigo ficou modesta de propósito: o escalonador iguala o melhor ganho fixo sem que alguém precise escolher qual é, e o ganho plausível a priori falha; o que ele não faz é superar uma constante bem escolhida.
A partida importa, mas não onde eu esperava
Oito poses de partida, três execuções cada: mais perto, mais longe, deslocado para cada lado, de frente para a parede, de costas, na diagonal. Vinte de vinte e quatro. A manipulação não dependeu de onde o robô começou: todas as vinte que chegaram ao refino de perto abriram a chave, com a lâmina entre vinte e sete e trinta e dois graus, e o refino fechou o yaw da parede em menos de dois graus em todas.
As quatro falhas foram outra coisa. Ontem o viés de nove graus no yaw da busca de longe tinha sido aceito e documentado por decisão do orientador, e a decisão estava correta para a pose padrão. De outras partidas o viés chegou a trinta e dois graus, e nesse tamanho o standoff intermediário sai torto o bastante para a tag sumir do quadro utilizável; a remedida fica sem amostras e a missão aborta antes de manipular. As quatro falhas foram exatamente as execuções com erro acima de vinte e sete graus. A pose de frente para a parede, com a tag já centrada e nenhum giro antes de medir, deu cento e quarenta e oito graus para uma parede a cento e oitenta, três vezes seguidas.
Duas correções, pedidas por ele assim que viu a tabela. A busca passa a amostrar só com a tag num anel do quadro, entre trinta e quarenta e três graus fora do eixo, girando a base até a tag entrar nele; e quando a remedida intermediária fica sem amostras, a aproximação gira para o lado da parede estimada até reencontrar a tag e mede de novo, em vez de abortar. Na revalidação, oito de oito: a pose de frente foi de zero para três em três, com erros de busca abaixo de quatro graus.
O que não ficou provado
Aqui é onde este diário precisa ser honesto. Tentei reproduzir o viés de propósito, forçando a busca a amostrar com a tag no centro do quadro, e ele não apareceu: duas execuções com o yaw certo. O fato empírico continua de pé, a raio zero vírgula dezoito sem girar deu cento e quarenta e oito graus três vezes e com o anel deu certo três vezes, mas a explicação que eu tinha dado de manhã, “vista frontal é ambígua”, não é suficiente. A regra se justifica pelos ensaios que falharam e pelos que depois passaram, não por um modelo do viés. E a recuperação da tag executa o caminho inteiro num teste forçado, gira, reencontra, centra, remede, mas nunca foi necessária depois que o anel entrou, e um resgate de ponta a ponta não foi demonstrado. Escrevi as duas coisas no artigo com essas palavras.
Sobrou também uma falha de manipulação sem causa. Numa execução a chave ficou em nove graus com a lingueta empurrada oito milímetros contra doze esperados; noutra, a chave abriu com os mesmos oito. A lingueta no fim do destrava não prevê a abertura, e o indicador que falta é da fase seguinte, a soltura.
O artigo, de nove para dez páginas
Três PRs no manuscrito. A subseção da manobra, que era placeholder, ganhou a máquina de estados, os limiares de histerese e o piso corrigido; a Seção V-A ganhou a aproximação de longe com uma tabela e as poses de partida com outra; o parágrafo que aceitava o viés de nove graus agora diz que foi a decisão errada e aponta para a bateria que mostrou isso. Por fim, a subseção do protocolo da chave, também placeholder, foi escrita com os números da missão real: a busca e seu anel, as duas etapas de aproximação, o refino que prevê a pose de manipulação, as nove fases numa tabela com offsets e tolerâncias por eixo, o critério observável do destrava, e o que “sucesso” significa na simulação, lâmina acima de vinte e cinco graus lida do ground truth, e na bancada, onde a abertura precisa de um sinal externo.
O que ficou
O punho simulado do RV-M2 continua esperando a rigidez do real, dado do orientador. A pasta de logs do ROS chegou a três gigabytes e não apago sem ele dizer. E ficou a lição do dia, que vale mais que as correções: uma decisão de aceitar uma limitação só vale para as condições em que foi medida. Bastou mudar a pose de partida para ela cair.
03 Set 2026
O Gatilho que Faltava, o Punho que Cedeu e a Regra que Virou Título
O dia começou com um ajuste de três milímetros nos arcos e terminou com setenta missões simuladas, oito PRs no robô, quatro no artigo e um título novo. No caminho, uma fase da tarefa que estava na descrição original e eu tinha apagado sem perceber, uma pergunta do orientador que reorganizou o artigo, e uma investigação que passou por três hipóteses erradas antes de encontrar um punho cedendo.
O gatilho que estava lá desde agosto
Eu ia corrigir a altura dos waypoints do arco quando o orientador parou o assunto: “o primeiro movimento deve ser um movimento bem acentuado em −Z, pois é aí que ocorre o destravamento do gatilho que trava a chave. O arco 1 não é um arco.” O repositório confirmou: a fase release, de quinze milímetros para baixo, existia desde a descrição da tarefa em 12 de agosto e desapareceu na reescrita em seis fases de 26 de agosto. Sem gatilho no modelo, nenhuma das missões sentiu falta dela.
Ele desenhou o mecanismo, fotografou a chave da bancada e filmou a abertura. O olhal de latão não está na lâmina: está numa lingueta com molas de arame, e uma aba dessa lingueta fica presa num laço em U do contato fixo. Puxar o olhal para baixo solta a aba; puxar para fora, ainda segurando, leva a aba por baixo do laço; depois a mola volta e o arco segue. Modelei a lingueta como junta prismática com mola, a aba e o laço com colisão, e passei pelos erros de sempre em ordem: a força de teste aplicada no pivô do link (torque zero), o Gazebo que não testa colisão entre links do mesmo modelo sem selfCollide, o serviço de força que aplica em coordenadas do mundo mesmo pedindo o frame do link, e a barra da lâmina que ficava embaixo do anel e virava batente para o degrau. Com os quatro corrigidos, o teste de bancada segurou vinte newtons sem descer e abriu com oito newtons de empurrão, e a missão fechou duas vezes seguidas com a chave em trinta graus.
A pergunta que reorganizou o artigo
“O manipulador não tem encoders, a movimentação será baseada num processo Fuzzy; a simulação está levando isso em consideração?” A resposta honesta, verificada no código: a arquitetura é sem encoder de ponta a ponta, mas a missão da chave movia o braço nas fases finas por posturas de IK remedidas pela T265, com o controlador Fuzzy calado. O caminho whole-body existia atrás de um parâmetro que o launch nem repassava. Ou seja, as vinte missões validavam a geometria, a percepção e a sequência, não o controlador que o braço real vai rodar. Isso foi para a Seção III-B e para a Introdução do artigo no mesmo dia.
Quatro baterias com a mesma régua
A partir daí o dia virou uma sequência de baterias de cinco, todas com o mesmo fixture e, salvo uma, a mesma tolerância por eixo. O whole-body puro fechou cinco de cinco com a tolerância esférica de vinte milímetros, e aquilo era artifício: o alvo de destravar já cabia na esfera, a fase fechava num segundo com a lingueta parada, e quem soltava o gatilho era a fase seguinte. Com a régua por eixo o mesmo controlador fez uma de cinco: a altura fechava, a profundidade estacionava a dez milímetros do alvo. A sonda da base descartou o plano de exclusão como culpado — o chassi nunca chegou a menos de sessenta e quatro centímetros do limite de cinquenta e cinco.
O orientador perguntou por um híbrido, e a divisão saiu das próprias baterias: fases de geometria (orientar, aproximar, inserir, capturar, desengatar) por posturas de IK, que fecham tolerância por eixo em duas iterações e alinham o degrau com o furo; fases de contato (destravar, liberar, os arcos) pelo laço Fuzzy, que insiste até o mecanismo ceder e traz a base para puxar. Cinco de cinco. A ponderação de juntas ciente de batente, tentada em seguida, não acrescentou nada: quatro de cinco, com a base presa no piso de dez milímetros por segundo dentro da banda de ganho pequeno do escalonador. Ficou implementada e desligada.
Ele perguntou se o híbrido cabia numa tese, e cabe se a contribuição for a regra e não o remendo: onde o alvo é uma posição observável com tolerância por eixo, modelo; onde o alvo é um evento no mecanismo que a ferramenta precisa provocar, servo. Cabia tão bem que virou o título: Geometry by Model, Contact by Servo. A Seção IV ganhou os pesos reais do Mamdani, tabela de funções de pertinência, base de regras e figura; a Seção V ganhou a tabela das cinco baterias.
O critério que a bancada consegue medir
Da lista do orientador, o primeiro item: a fase de destravar fechava por chegar a uma altura-alvo relativa ao olhal estimado, e nunca destravava sozinha. Agora a captura registra a altura da ponta pela T265 e a fase só fecha quando a ponta desceu um curso mínimo a partir dali. Com treze milímetros, quatro de cinco e a lingueta em oito; a ponta desce três a cinco milímetros a mais do que o anel, por contato ou deslizamento. Com dezoito, cinco de cinco e, pela primeira vez em cinquenta missões, a lingueta acima de dezesseis milímetros ao fim da fase em todas. Na bancada o número se recalibra com o gatilho audível; a medida é a mesma.
O punho que cedeu
O segundo item era a profundidade que estacionava no whole-body. Três hipóteses caíram em sequência: o plano de exclusão (sonda), o punho no batente por comando (pesos), a base cara demais (base barata quando uma junta encosta — nunca disparou). O diagnóstico no controlador mostrou o que nenhuma delas explicava: o controlador mandava o J4 descer, o setpoint ia de setenta e nove para sessenta e seis graus, e o J4 real subia até cento e dez com o esforço saturado em vinte newton-metro. A fase empurrava o olhal contra o fim de curso da lingueta com controle de posição, o braço acumulava força e a junta mais fraca cedia. É um artefato do braço simulado, o RV-M2 não retro-aciona, mas a causa valeria na bancada.
O remédio foi parar quando a descida medida estagna: cinco de cinco, fase em quatro segundos e o esforço do punho em dois newton-metro. O preço é fechar cedo, com o anel em cinco milímetros, e a fase seguinte terminar a soltura. Tentei esticar a janela para forçar quinze milímetros e o punho colapsou de novo: quando o anel bate no fim de curso a ponta continua descendo, pelo colapso do J4, e a estagnação nunca aparece. O robô tombou. Voltei aos parâmetros seguros e deixei a decisão onde ela pertence: modelar o punho simulado com a rigidez do RV-M2 real, com dado do orientador.
O que ficou
O viés de nove graus no yaw do SEARCH, ambiguidade do PnP numa tag de vinte e três pixels que o REFINE corrige de perto, foi aceito e documentado por decisão dele: “não vale a segunda vista”. A linha de base com ganhos fixos, três baterias de cinco substituindo o Mamdani por constantes, estava rodando quando este diário foi escrito. Depois vêm as poses de partida do robô, que a bancada consegue reproduzir com a chave fixa, e o dado do punho.
Sobre a chance de aceitação do artigo, a resposta que dei foi a mesma que este diário sustenta: hoje baixa em periódicos de primeira linha, razoável com dez ensaios na bancada. O mérito conceitual está apoiado; o que decide é evidência.
02 Set 2026
A Câmera nos Furos, o Dedo de Dez Milímetros e o SEARCH que Media na Borda
O dia tinha um objetivo declarado — revalidar a missão inteira depois de remontar a câmera — e terminou com três missões completas seguidas, a última sem nenhum botão de teste. No meio, duas trilhas falsas que me custaram horas e uma descoberta que eu já tinha feito ontem e não reconheci.
A câmera onde os furos mandam
A T265 estava modelada quatro centímetros à frente do suporte, olhando trinta graus fora da direção da ferramenta, e desenhada com uma malha que não era uma T265: 148 por 30 por 28 milímetros, contra os 108 por 24,5 por 12,5 da peça de verdade. O orientador comparou a simulação com a câmera na mão e apontou os três problemas em sequência — posição, modelo e o lado do conector — e me indicou a documentação do fabricante.
Ela resolveu a única pergunta que eu ia ter que chutar: a origem do sensor é o centro de rastreamento, definido como o ponto médio entre as duas lentes fisheye. Nem o centro do corpo, nem um canto. O suporte do punho tem dois furos M3 espaçados de exatamente 50,5 milímetros — o padrão de fixação da T265 — sobre um plano de apoio; face das lentes é apoio mais espessura. A malha oficial da Intel, que o próprio repositório do fabricante distribui, tem as duas lentes separadas por 64,74 milímetros. Posicionada pelo ponto médio delas, cada lente cai a 32,37 milímetros do centro do link. A extrínseca que eu havia medido na bancada com o driver real, semana passada, era 32,0. Quatro décimos de milímetro entre a malha da Intel e a régua — e a montagem inteira validada por duas fontes que nunca se viram.
Faltava o conector, que estava do lado errado. A única rotação rígida que troca o lado do conector sem tirar a câmera de mira é meia-volta em torno do eixo óptico; a consequência é que a lente que enxerga atravessou 64 milímetros para o outro lado do suporte. Eu tinha preservado a rolagem antiga por conservadorismo, e conservadorismo não é dado de bancada.
De quebra, a malha do suporte trazia duas câmeras desenhadas que não existem no robô — duas D435 de enfeite, uma das quais eu confundi com a T265 no início da tarde. Saíram, por componentes conectadas, com os furos de fixação preservados.
Um dedo de dez milímetros
O orientador reviu a ferramenta: haste e degrau passam de 10,4 por 20 e 20 por 17 para dez por dez, e o afunilamento entre o garfo e a haste é uma rampa, não o bloco de cantos vivos que eu havia modelado. O que a redução compra é o que ontem decidia a tarefa: um degrau de 20 por 17 num oval de 30 por 40 só passava com menos de vinte graus de rolagem, e foi isso que fixou a distância de engate. Um quadrado de dez tem diagonal de 14,1 milímetros e cabe no vão de trinta em qualquer rolagem. Na prática, as missões de hoje atravessaram o aro com o degrau rolado entre dezoito e vinte e quatro graus, sem travar uma vez.
Mudança de sete milímetros no comprimento total, zero na cinemática — o ponto que a missão persegue é o mesmo. A descida da captura, que sai de uma fórmula escrita no próprio arquivo de configuração, passou de 11,5 para 15 milímetros. As tolerâncias eu deixei intactas de propósito: as folgas dobraram, mas afrouxar sem medir é o que aquele arquivo existe para impedir.
As compensações que viraram erro
A primeira missão da revalidação abortou na etapa intermediária: o robô parou onde achava que estava a 1,3 metro do olhal e não viu mais a tag. A cinemática direta apontou o motivo antes de qualquer teste: a postura de busca tinha J1 a −30,3 graus, e o comentário no arquivo dizia com todas as letras que era para pôr o eixo óptico à frente da base — compensando a câmera torta. Com a câmera reta, esses trinta graus apontavam a câmera para o lado. E J5 a −90 graus, que endireitava a câmera inclinada, com a câmera reta passou a só girar a imagem: e girava o dedo fixo para exatamente a faixa do quadro por onde a tag entra.
Para não seguir por dedução, montei uma bancada de pose: teleporte do robô para poses escolhidas, com a verdade do simulador como régua, contando detecções e poses aceitas. Girando a base a partir da parada de busca, com J5 em −90 graus:
| fora do eixo | 71° | 60° | 49° | 37° | 24° | 11° | 3° |
|---|---|---|---|---|---|---|---|
| detecções em 5 s | 60 | 52 | 0 | 0 | 0 | 27 | 73 |
| aceitas | 16 | 6 | 0 | 0 | 0 | 0 | 0 |
Dois mecanismos, ambos novos com a câmera olhando ao longo da ferramenta. Entre 24 e 49 graus, o dedo fixo tapa a tag — o quadro de 37 graus mostra a peça amarela exatamente em cima dela. Perto do centro, a tag é detectada em todos os quadros e rejeitada em todos: a um metro e meio ela tem 24 pixels de lado, e uma tag planar pequena vista de frente tem duas poses igualmente plausíveis, coisa que o filtro de ambiguidade descarta como deve. Foi essa segunda a razão de o REFINE ter ficado vinte segundos cego no standoff: a tag a 22 graus, na faixa da ferramenta.
Com J5 em zero a faixa de oclusão some. A mesma curva: 47, 41 e 22 poses aceitas a 48, 35 e 22 graus, com a parede medida em 3,07, 3,06 e 2,96 metros contra 2,98 reais. E o REFINE, a 49 centímetros: 74 detecções, 74 aceitas, erro de dois milímetros e meio. J1 e J5 foram a zero na postura, com a medição no comentário.
O SEARCH que media na borda
Sobrou a busca. Ela parava na primeira detecção, com a tag entrando pela borda da fisheye, a mais de setenta graus do eixo — e ali a projeção equidistante mede curto: 0,4 a 1,0 metro a menos numa parede a dois. De manhã, com a câmera antiga, a busca acertava dentro de nove centímetros. Não era que a borda medisse melhor antes; era que a tag saía dela antes da coleta, e hoje ficava. O erro se propagava: o robô parava um metro longe demais para a etapa seguinte, na zona onde a tag pequena vira ambígua, e a remedida morria. Quatro missões abortaram assim.
A correção é geométrica e pequena: avistada a tag, a base continua girando devagar até a tag cair para dentro do quadro, e só então para e mede. Não é o rastreamento por punho que abandonei semana passada — nenhuma junta do braço entra na malha; é a base concluindo o giro que já fazia. A primeira versão parava com a tag a 52 graus, e duas coletas seguidas de 25 segundos renderam uma e zero amostras: detectada em cada quadro, rejeitada em cada quadro, com a razão de ambiguidade congelada em 1,37 — quadros idênticos não saem do limiar. A 43 graus, dentro da faixa que a curva apontava, a coleta rendeu na primeira parada: 25 amostras em três segundos e meio, parede em 3,11, e a missão fechou em 127 segundos de simulação, contra 177 da versão anterior.
Três missões completas em sequência: a primeira ainda com um botão de teste compensando o viés da busca, as duas últimas sem botão nenhum. Lâmina aberta a 26,6, 27,0 e 26,6 graus por contato.
Um crash escondido e um chão que já tinha explicação
No meio da tarde o orientador viu o robô, parado no fim da manipulação, avançar e colidir com a chave, e propôs limitar a segunda junta para o punho não chegar tão perto. Fui ao registro antes de mexer na junta: o nó da missão tinha morrido no primeiro instante da manipulação — uma função chamada sem ter sido importada, num trecho que entrou ontem e que nenhuma execução tinha exercitado desde então. Com o nó morto, ninguém mandava parar; o controlador de corpo inteiro ficou com o último alvo e empurrou a base 28 centímetros para dentro da fixture. A postura que o orientador viu é a mesma de ontem e da manhã inteira; o que era novo era o crash. Uma linha de correção.
A outra observação foi a deriva: o robô parado, sem comando, andando para a frente. Medi de novo — 2,58 milímetros por segundo, rodas rolando, zero mensagens de comando — e gastei dois restarts do simulador testando atrito de junta e taxa de atualização do plugin de tração. Nenhum efeito. Só ao reler a entrada de ontem para escrever esta é que caiu a ficha: a causa já estava identificada — o parâmetro que define a direção de referência do atrito das rodas, comentado no modelo com um link para o relatório do fabricante — e a âncora da missão foi declarada contenção, não solução. Passei pelo parâmetro comentado durante o dia sem reconhecê-lo. Fica o registro do erro de método: antes de reinvestigar um sintoma, reler o que já foi escrito sobre ele.
A saída que varria a parede
Última observação do dia, na terceira missão bem-sucedida: depois da abertura, a saída colide com a chave. A captura de contatos da simulação mostrou o punho contra a lâmina e a placa da chave nos dois segundos seguintes ao comando de recolher o braço, e a cinemática direta sobre o rastro de juntas explicou: a saída existente recua a ponta doze centímetros pelo eixo do furo — que é paralelo à parede — e depois interpola as três juntas do braço rumo à postura recolhida. Nesses dois segundos a ponta vai dez a doze centímetros para dentro da placa, a 85 centímetros de altura, antes de subir.
A correção reaproveita o mesmo primitivo do puxão de abertura: a base recua 25 centímetros pelas rodas, com o braço parado, entre a saída pelo eixo e o recolhimento. Está implementada e em teste no momento em que escrevo; o critério é zero contatos entre robô e chave depois da manipulação concluída.
Onde parei
Quatro commits locais na branch de revalidação, aguardando a saída limpa para virar PR: a correção do crash, a postura de busca sem as compensações, a regra de parada da busca, e o recuo antes de recolher. Em aberto: o yaw da parede medido pela busca sai onze graus fora e a remedida absorve — registrado, não explicado; a tolerância de eixo de oito milímetros travou uma travessia em 8,5 hoje, com as folgas dobradas desde a ferramenta nova; e a decisão de declarar a direção do atrito das rodas, que corrige a deriva na origem ao custo de revalidar a navegação.
Adendo da noite: a deriva resolvida na origem
O PR do dia foi mergeado e o orientador pediu, antes de qualquer investigação nova, que eu fechasse a ponta que a entrada de ontem deixou aberta: declarar a direção de referência do atrito das rodas e medir.
A direção é expressa no frame da colisão, e o cilindro da roda no modelo é girado noventa graus — então “eixo Y” ou “eixo Z” não se decide lendo o arquivo; decide-se medindo. Robô parado, vinte segundos de assentamento para tirar o pouso do spawn da conta, janelas de dez segundos, deslocamento pela verdade do simulador. Sem o parâmetro: 25,8 milímetros por janela, como ontem. Com a direção errada: 25,7 e 25,8 — zero efeito. Com a direção certa: 9,8, depois 6,0, depois 0,5, 0,1, 0,1 e 0,2 milímetros por janela. As primeiras janelas eram a cauda do pouso decaindo; em regime, a deriva caiu entre cinquenta e duzentas e cinquenta vezes.
Dois detalhes que valem o registro. As rodas continuam girando devagar no lugar — o motor de velocidade do plugin de tração não segura zero exatamente —, mas o contato deixou de converter esse giro em avanço do corpo, e a odometria que a missão usa vem da verdade do simulador, não do plugin, então nada é contaminado. E a vibração vertical do contato, que o próprio arquivo apontava como suspeita de uma correção anterior, é nula: um micrômetro pico-a-pico. O que sobrava não era vibração retificada; era a pirâmide de atrito apontando para um lado arbitrário a cada contato.
O custo previsto ontem — revalidar a navegação com o contato novo — foi pago na mesma noite: busca, duas etapas de aproximação, standoff, sete fases de manipulação, saída sem contato e retorno, missão completa. A âncora que corrigia a base entre dezoito e trinta vezes por coleta parada corrigiu uma a três. Seis missões completas seguidas no dia, e o parâmetro que estava comentado há um ano com o link para o relatório do fabricante agora está declarado, com a tabela da medição ao lado.
O próximo fio, já combinado: o yaw da parede medido pela busca, que sai entre onze e dezesseis graus fora e hoje a remedida absorve.
Adendo, mais tarde: o dither e o yaw que não cede
Resolver a deriva expôs um efeito que ela escondia. Com o robô agora perfeitamente parado, os quadros da câmera ficam idênticos, e a razão que decide se uma pose da tag é confiável ou ambígua congela: a mesma pose é aceita em todos os quadros ou rejeitada em todos, conforme o lado do limiar em que caiu. Na bancada, em três de sete poses a tag foi detectada em cada quadro e aceita em nenhum. Os dois milímetros e meio por segundo que eu acabara de eliminar eram, sem que ninguém soubesse, o que desfazia esse congelamento.
A correção é pequena e deliberada: durante cada coleta de amostras a base gira devagar, cinco centésimos de radiano por segundo, invertendo o sentido a cada segundo e meio. São quatro graus de rumo para cada lado, que a navegação seguinte refaz de qualquer jeito. Na missão de validação nenhuma coleta travou — a busca rendeu vinte e três amostras em oito segundos, a remedida e o refinamento dez em um segundo cada — e a chave abriu.
O que o dither não corrigiu merece o registro mais honesto do dia. A orientação da parede medida na busca continua uns nove graus fora, como nas execuções anteriores. A bancada explicou por quê: para uma tag de vinte e poucos pixels, a rotação em torno da vertical é a componente que os quatro cantos menos restringem, e a estimativa alterna de sinal entre poses vizinhas — dezessete graus para um lado, dezessete para o outro — com a posição boa nas duas. Girar a base faz os dois lados aparecerem na coleta; mas o filtro que rejeita amostras longe da mediana descarta o lado minoritário, e a média fica presa no lado que a mediana escolheu. É uma limitação da tag de cento e trinta e dois milímetros vista a quase dois metros, e a missão já convive com ela: a remedida a um metro e trinta entrega dois décimos de grau. Fica na mesa a escolha entre aceitar, tirar o filtro só na busca, ou desambiguar com uma segunda vista.
Sete missões completas seguidas no dia.
01 Set 2026
O Chão que Anda Sozinho, o Dedo do Lado Errado e o Olhal Tapado
O dia começou com uma reclamação simples do orientador: o robô não para. A missão estava pausada, o braço imóvel, e ele via a base andando na tela. Eu tinha uma explicação pronta na véspera — o plugin de tração do simulador não tem tempo-limite de comando, e mantém a última velocidade recebida para sempre. Escrevi isso em comentário no código, com convicção, e passei o dia inteiro descobrindo que estava errado.
Sete hipóteses, sete refutações
Medi a base parada: 2,6 milímetros por segundo, para a frente, em linha reta. São dezesseis centímetros por minuto de robô que ninguém mandou andar.
A primeira hipótese caiu rápido. Reafirmei o comando de parada cinco vezes por segundo e a deriva não mudou uma casa decimal. A segunda foi que as rodas não tinham atrito nas juntas — fui conferir e elas têm, com amortecimento e atrito declarados no modelo. A terceira foi o peso do braço estendido vencendo esse atrito: medi com o braço recolhido e com o braço estendido, 2,56 e 2,66 milímetros por segundo. O braço é inocente.
A quarta foi que era assentamento da física depois de um teleporte, coisa que decai. Medi dez janelas seguidas de oito segundos sem tocar em nada: constante, sem decair. A quinta foi chão inclinado — girei o robô em quatro orientações e o rumo da deriva acompanhou o robô, não o mundo, o que descarta gravidade. A sexta foram os controladores do braço segurando as juntas: parei todos e a deriva ficou em 2,646 contra 2,643. A sétima foi erro numérico do solver de contato — multipliquei as iterações por vinte e o efeito caiu quatro por cento.
Derrubei o sistema inteiro, nó por nó, até sobrar só o simulador. A deriva continuou.
No meio disso houve uma medição que me enganou e que merece registro. Numa das rodadas o robô ficou absolutamente parado, zero milímetro em oito segundos, e eu quase fechei o caso. A diferença entre aquela medição e todas as outras era que eu não havia teleportado o robô antes dela. O que eu tomei por resultado limpo era o caso especial; o resto era a regra.
A causa está no modelo de contato das rodas do Pioneer, e estava documentada à vista: um parâmetro que define a direção de referência do atrito está comentado, com um link para o relatório do próprio fabricante. Sem ele, o motor de física escolhe uma direção arbitrária a cada contato entre o cilindro da roda e o plano do chão, e o resíduo dessas escolhas é retificado num avanço constante. O próprio arquivo já registrava o mesmo sintoma quando ele era dez vezes maior, com uma correção parcial de um ano atrás. Os 2,6 milímetros por segundo são o que sobrou.
A deriva contaminava as minhas próprias medidas
O incômodo visual era a menor parte. A cada segundo parado, o robô se desloca dois milímetros e meio — e as minhas rotinas de medição funcionam posicionando e esperando. A coleta de amostras da tag leva quinze segundos: quatro centímetros de deslocamento durante a medição, com cada amostra tirada de um ponto diferente.
Isso não vira ruído que a média cancela. Vira viés na direção do avanço, porque a pose da parede é calculada a partir da pose do robô. Vários números finos que reportei este mês valem menos do que eu disse.
Escrevi uma âncora: o programa guarda a pose e devolve a base ao lugar sempre que ela escapa mais de dois milímetros. Não é a correção de verdade — a correção é declarar o parâmetro que falta no modelo, o que mexe na tração das quatro rodas e obriga a revalidar a navegação. É contenção, e está marcada como tal. Validada no pior caso: de 128 milímetros para 3 em vinte segundos, sem o braço sentir nada.
A ordem que derrotava o próprio objetivo
Com a base contida, o orientador apontou outra coisa na tela: ao fim do posicionamento a ferramenta já estava a quinze centímetros do olhal, apontada na direção certa — e então o braço se rearranjava inteiro. “Bastava orientação e deslocamento.”
Fui ao registro. A etapa de posicionamento resolve a cinemática para a pose atual da base, deixa a ferramenta pronta, e só então a base avança dezoito centímetros com as rodas. Esse avanço invalida a solução recém-calculada, e a primeira fase precisa refazer o braço todo — cento e cinquenta e um graus de junta somados, para um deslocamento líquido de quatro milímetros em relação ao alvo.
O detalhe que dói é que o comentário no código dizia, com todas as letras, que o posicionamento existe para que o rearranjo grande aconteça longe da parede. Do jeito que estava, ele acontecia depois do avanço, colado nela. A ordem derrotava o próprio objetivo declarado.
Passei a resolver a cinemática para a pose que a base vai ter. O braço é armado uma vez, longe, e a base entra por baixo dele. Os cento e cinquenta e um graus viraram nove.
“Alinhado” apontando para o lado oposto
O achado mais sério do dia veio de uma desconfiança do orientador sobre o ramo de solução. Comparei as duas configurações que o braço vinha alternando entre execuções, com a mesma geometria de entrada, e olhei para onde o dedo apontava em cada uma.
Produto escalar de menos zero vírgula nove quatro sete. Cento e sessenta e um graus entre elas: lados opostos.
E nas duas a cinemática reportava resíduo zero e “dedo a nove décimos de grau do eixo do furo”. Porque ela media o ângulo contra um eixo, sem sinal — para ela, girar o punho cento e oitenta graus era indiferente. Isso é verdade para uma haste simétrica. A ferramenta deste robô tem um dedo só, desde que apagamos o dedo fantasma conferindo com as fotos. Os dois sentidos não são a mesma coisa: num deles o degrau abraça o anel, no outro aponta para fora e só consegue empurrar.
Isso é candidato a explicar uma observação que o orientador tinha feito semanas atrás e que eu nunca soube encaixar: “a ferramenta não engatou no olhal, ela puxou por trás do olhal”. Não era imprecisão de milímetros. Era o dedo do lado errado, com a cinemática satisfeita.
Passei a direção a vetor com sinal. Verificado na geometria real que produzia o espelhamento: sem sinal o punho ia para menos cento e setenta e nove graus, com sinal vai para menos três — com o mesmo resíduo de posição. O giro de cento e oitenta graus não custava exatidão nenhuma; era escolha gratuita entre duas soluções que a cinemática julgava equivalentes.
Aqui cabe uma confissão de método. O primeiro teste que fiz para validar essa correção não provou nada, e eu quase o reportei como evidência: usei uma geometria sintética, as quatro variantes convergiram para a mesma solução, e o que eu ia chamar de “funciona” era apenas o multi-start interno da cinemática dominando o experimento. Só o teste com a geometria real da execução separou os casos.
O olhal que a própria chave tapava
À tarde o orientador olhou a cena e disse: “no olhal tem uma parte da chave que pode colidir com a haste. O olhal precisa estar livre.”
Estava certo, e era erro de modelagem, não de ajuste. O anel é fixado no topo da lâmina, e a barra da lâmina ia do pivô até exatamente esse ponto — ou seja, terminava no centro do furo e tapava a metade de baixo dele. A ferramenta tinha uma fresta de vinte milímetros por cima, quando o oval oferece quarenta e seis. Na chave real o olhal é uma alça na ponta da lâmina, e a alça é vazada.
Encurtei a barra até a borda inferior do anel. A distância pivô-olhal continua sendo os duzentos milímetros de bancada, e o centro do olhal continua a oitocentos e cinco milímetros do chão, que é a medida que ele mesmo tirou com trena.
Tolerâncias maiores que aquilo que protegiam
Duas correções do mesmo tipo, e é um tipo que eu não tinha nome para antes deste dia.
A trajetória pedia que a ferramenta passasse dez milímetros acima do centro do furo, exatamente para escapar da barra. Mas a tolerância vertical era de dez milímetros para cada lado — ou seja, admitia terminar dentro da barra. Medido: a fase fechou com quinze milímetros de resíduo, dentro do permitido, e a ponta ficou três milímetros abaixo do centro, invadindo a lâmina em seis.
A outra tolerava quarenta milímetros ao longo do eixo de inserção, com a justificativa de que o degrau tem trinta milímetros de comprimento. Isso vale para ajustar a profundidade de quem já está dentro do furo. Aplicado a quem está fora, aceita como sucesso uma haste que nunca entrou — e foi o que aconteceu: a fase declarou vitória a trinta milímetros de distância e parou de iterar com três iterações ainda disponíveis.
A regra que ficou: uma tolerância nunca pode ser maior que a margem que ela existe para proteger.
Duas famílias, e a que não se sustenta
Sobrava entender por que o alinhamento do dedo, tão bom no início, degradava fase a fase em algumas execuções e não em outras. Comparei quatro execuções e o padrão apareceu numa junta só, a do punho:
Nas execuções em que ela saía positiva, o desalinho crescia — nove, doze, catorze, dezenove graus. Na única em que saiu negativa, ficou parado abaixo de um grau do começo ao fim. Duas famílias de configuração que colocam a ferramenta no mesmo lugar e se comportam de forma completamente diferente ao longo da trajetória.
A escolha entre elas era sorteio: a postura que eu usava como semente da cinemática está equidistante das duas. Troquei por uma postura que vive dentro da família boa, e a escolha deixou de ser acaso — duas execuções seguidas caíram do lado certo.
Só que na fase de inserção o braço saltou de volta para a outra família: cento e quarenta e dois graus de reconfiguração no meio do movimento. Foi isso que o orientador viu como “o manipulador caiu e depois ficou chocando com a chave” — e os registros de contato confirmam: mil e oitenta e cinco toques na barra da lâmina contra quatro no anel.
A causa era minha correção pela metade. A âncora que eu tinha posto governava só a decisão inicial; cada fase depois resolvia a cinemática com busca global, e a busca global encontrava uma solução de custo ligeiramente menor do outro lado e migrava. Separei os papéis: busca global uma vez, no início; refinamento local nas fases, semeado apenas com a postura atual. Com isso sair da família deixou de ser possível — e não saiu mais.
Três instrumentos que mentiram, e um erro de referencial que era meu
Já registrei aqui, num dia anterior, que meus instrumentos de medição mentem. Aconteceu de novo, três vezes.
Li os registros de contato pelo nome da colisão e concluí que a ferramenta estava tocando o anel. Estava tocando dois segmentos vizinhos do anel — que é a assinatura de uma haste apoiada por fora, atravessada sobre eles, não enfiada no furo. Segmentos opostos seriam engate; vizinhos são apoio.
Comparei a ponta com a posição do olhal fechado enquanto a lâmina já estava a quatro graus e o olhal havia saído do lugar. É exatamente o mesmo erro que já me enganou antes e que estava anotado como lição aprendida. Repeti.
E consultei a pose de um elo que o simulador funde com outro por junta fixa: o serviço respondeu com sucesso e devolveu a origem do mundo. Números limpos, plausíveis, e completamente falsos.
O pior dos três foi meu, e não do instrumento. Ao investigar se a ferramenta chegava perpendicular ao furo, supus qual eixo do anel era o eixo do furo, calculei, e anunciei uma discrepância de noventa e um graus — um achado que reorganizaria o projeto inteiro. Fui conferir no arquivo antes de seguir: o comentário dizia, explicitamente, o contrário do que eu supus, e registrava que o próprio orientador havia corrigido essa mesma confusão semanas antes. O eixo que o programa mira está a um grau do eixo verdadeiro. O achado era meu erro, com dois algarismos significativos.
O clique que matava a execução
Duas execuções morreram por um motivo que eu mesmo criei e depois consertei pela metade.
Os laços de espera do programa dormem em tempo simulado. Quando o simulador é pausado — um clique na interface, que é o gesto mais natural do mundo para olhar a ferramenta parada —, o relógio da simulação para, o laço nunca acorda, e a missão fica irrecuperável: o processo vivo, respondendo, e surdo ao comando de continuar. Corrigi o laço da pausa e deixei o da coleta de amostras. Na execução seguinte ele travou exatamente na coleta.
Agora os laços de espera usam relógio de parede e os de orçamento seguem em tempo simulado — não faz sentido queimar prazo esperando por amostras que não podem chegar com a física parada.
O painel, no meio disso, pareceu travar. Não travou: ele funde cada mensagem no que já tinha e sobreviveu a cinco lançamentos seguidos, então mostrava campos de uma execução morta ao lado dos da viva. A missão passou a carimbar um identificador e o painel zera quando ele muda.
Onde parei
A percepção fechou o dia madura. Oito execuções completas até a etapa de medição fina, todas com erro de nove milímetros ou menos na posição do olhal, partindo de condições iniciais que variaram de zero a cento e setenta e dois milímetros e de dois décimos a quarenta graus de erro na primeira medida. Uma execução abortou por perder a tag de vista no deslocamento intermediário, e esse é o limite real de recuperação da arquitetura — que agora está medido.
A chave continua fechada.
O ponto onde o processo quebra está identificado e é geométrico. No waypoint de inserção, o braço não consegue posição e alinhamento ao mesmo tempo: a cinemática entrega oito décimos de milímetro de resíduo com catorze graus de desalinho, e sobre os noventa milímetros de percurso isso são vinte e três milímetros de desvio lateral, contra dezoito de meio-eixo do oval. A haste encosta na borda, trava, e empurra a lâmina — o anel foge no arco e o alvo, calculado com a chave fechada, vira um fantasma.
São seis vínculos sobre cinco graus de liberdade. Quando a posição fica difícil, quem cede é a direção — exatamente como a formulação foi construída para fazer. Não é defeito de implementação; é o problema estando mal posto naquele ponto do espaço de trabalho. E o que determina se aquele ponto é fácil ou difícil é a pose da base, que é por onde continuo.
Fecho com um saldo que já vi antes neste diário e que continua desconfortável de escrever: oito correções entraram e estão verificadas, a percepção está sólida, o braço deixou de dar saltos de cento e quarenta graus perto da chave, o olhal está livre pela primeira vez — e a chave não abriu. A diferença em relação às vezes anteriores é que desta vez eu sei exatamente onde ela não abre e por quê, com número. Isso não é o mesmo que resolver, mas é a única coisa que torna a próxima tentativa diferente da última.
Adendo da tarde: o mapa que mostrou onde o robô devia parar
Depois de publicar o texto acima, ataquei a única frente que tinha ficado nomeada: a pose de aproximação da base.
A pergunta era geométrica e não precisava de simulação. Para cada pose candidata da base, dá para resolver a cinemática de todos os pontos da trajetória e ver que alinhamento o braço consegue entregar em cada um. Varri distância e deslocamento lateral, reproduzindo exatamente a cadeia que a missão executa, e anotei o pior desalinho ao longo das sete fases — porque basta uma ruim para a haste bater na borda.
O mapa mostrou duas coisas, e a principal ninguém estava olhando.
O robô estava manipulando de lado. Ele mede de frente para a tag, que fica dezessete centímetros ao lado do olhal — decisão certa, tomada semanas atrás, que tirou a tag da periferia da lente e ganhou uma ordem de grandeza na percepção. E vinha manipulando da mesma pose. Havia um comentário no código dizendo que a base assumiria o deslocamento lateral por navegação depois de medir; essa parte nunca tinha sido escrita. Na mesma distância, sair dos dezessete centímetros para zero vale treze vírgula seis graus de desalinho contra quatro.
A segunda é a distância, que tem um degrau entre 62 e 66 centímetros: abaixo dele tudo melhora. Combinando as duas, o pior caso cai para um vírgula cinco grau — dois milímetros e meio de desvio ao longo do percurso de inserção, contra dezoito milímetros de meio-eixo do furo. Folga de sete vezes, onde antes faltava.
O que me deu confiança no mapa não foi ele ser bonito, foi ele prever: no ponto de operação previa 13,6 graus e o robô, ao vivo, mediu 14,2. Implementei as duas mudanças e o desalinho caiu de sete graus para quatro décimos no posicionamento e um décimo na primeira fase.
E a chave não abriu.
O braço obedece parado e se debate andando
Com o alinhamento resolvido, a fase de inserção falhou exatamente igual: a ponta trava a trinta milímetros do furo e a junta da base fica devendo cada vez mais a cada iteração. Minha explicação anterior — que o desalinho fazia a haste bater no anel — caiu: com meio grau ela trava do mesmo jeito.
Fui medir o que eu vinha supondo. Comandei o braço para sete posturas conhecidas, em campo aberto, e o erro máximo foi de três graus, tipicamente abaixo de um. Repeti na pose exata da falha, com a sequência exata de posturas da execução que falhou: quatro décimos de grau. Repeti com o anel no caminho e com a âncora teleportando a base cinco vezes por segundo: igual. O braço vai onde é mandado.
Na missão ao vivo, o mesmo braço acumula quarenta e dois graus de resíduo.
Instrumentei a execução e gravei tudo a vinte hertz — comando, verdade do simulador, estimativa, pose e velocidade da base. O traço desmontou a minha leitura: a junta da base não está travada, ela atrasa quase dez graus por seis segundos e depois passa vinte e sete graus do alvo, enquanto as outras três varrem trinta graus cada em três segundos. O braço não está rígido; está se debatendo.
E há uma anomalia que eu não sei explicar ainda: durante toda essa janela, uma das juntas do punho é reportada com uma volta inteira a mais — quatrocentos e quarenta e quatro graus onde a pose física é oitenta e quatro, num eixo cujo limite mecânico é cento e dez. O valor já estava assim antes de a missão começar, e nenhuma outra junta viola limite em momento algum. Quem lê esse número — o estimador, a ponte para os controladores, a própria missão — enxerga o braço em outro lugar.
Descartei nesta rodada, com medição: contato (zero contatos com o anel na posição fechada), a âncora, o controlador de corpo inteiro (que está desligado nessa etapa), o atrito das juntas (é zero no modelo), os ganhos, o erro estacionário sob carga, e o serviço de reposicionamento que eu suspeitava introduzir o salto. Nenhum é.
Fecho o dia sem a origem dos 360 graus. Mas fecho com o alvo mudado: o obstáculo não é geometria. A percepção entrega o olhal com três milímetros, o alinhamento está em quatro décimos de grau, o furo está livre, o braço não troca mais de configuração no meio do caminho — e a tarefa continua travando por um problema de controle que só se manifesta com a missão inteira rodando, e que nenhuma réplica estática reproduz.
É um lugar desconfortável para parar e um lugar honesto. Passei o dia eliminando suspeitos em condições que não falham, o que é uma forma elegante de não descobrir nada. O próximo passo é o oposto: monitorar aquela junta do primeiro instante do simulador até a falha e pegar o instante exato em que a volta aparece. Com o instante, o culpado costuma aparecer sozinho.
27 Ago 2026
Quando o Instrumento Mente — Um Alarme Invertido, Quatro Vieses e uma Ferramenta de Lado
O dia era para ser de arrumação. Na véspera fomos à bancada com uma trena e voltamos com números de verdade: o olhal está a 80,5 centímetros do chão e a 13 da parede, o furo dele não é redondo mas um oval de 40 por 30 milímetros, a tag de referência fica a 17 centímetros do olhal e na mesma altura, e a plataforma do robô foi alongada 9 centímetros na frente. Restava transferir isso para o modelo e ver o que mudava.
Mudou mais do que eu esperava, e quase nada pelo motivo que eu imaginava.
Comecei pelas fotos do robô real. Numa delas, a garra: só uma das castanhas tem dedo montado, e é nela que a peça do orientador é parafusada. A outra está nua. O modelo desenhava as duas desde sempre — um dedo fantasma que o robô não tem, ocupando espaço na simulação. Apaguei.
Depois o furo. Enquanto ele era o círculo de 56 milímetros que eu havia inventado, a ferramenta tinha 18 milímetros de folga de cada lado. No oval medido, a folga real é de 5 milímetros. Essa diferença não é cosmética: é ela que separa uma tarefa que perdoa de uma que não perdoa, e é ela que expôs tudo o que veio depois.
A ferramenta chegava de lado
Com o furo apertado e a abertura acontecendo por contato de verdade, a chave continuava sem abrir. Medi a atitude da ferramenta ao chegar no anel, lendo direto da árvore de transformações em vez de deduzir da minha própria cinemática — e o número foi de outra ordem de grandeza do que eu procurava. O dedo que deveria atravessar o furo estava a oitenta e nove graus dele. Não desalinhado: perpendicular. Em todas as fases, em todas as execuções.
A causa era estrutural e estava documentada no próprio arquivo, à vista: a cinemática inversa da manipulação resolve só a posição da ponta. A atitude sai como o ramo de solução calhar de entregar. Com cinco juntas e três restrições de posição, sobram dois graus de liberdade — exatamente o necessário para fixar a direção de um eixo. Escrevi essa versão, e o ângulo caiu de noventa graus para dois.
O orientador então observou algo que eu não teria visto olhando números: a trajetória passava por baixo da chave. A primeira fase já ficava nove centímetros ao lado do olhal, mas o braço chegava lá vindo do recolhido, subindo rente ao mecanismo. Acrescentamos uma fase que sobe e alinha a ferramenta quinze centímetros afastada da parede, e só então entra de lado. A aproximação deixou de ser de baixo para cima.
O alarme que ficava mais confiante quanto maior o perigo
No meio disso, o orientador avisou que o robô estava encostando na parede e que o laser parecia não funcionar. Medi: o laser estava perfeito. Ele reportava a parede a 40 centímetros do centro da base, contra 40 centímetros calculados da posição real — cinco milímetros de diferença.
O defeito estava no meu código. O nó de segurança descartava, como sendo estrutura do próprio robô, todo retorno abaixo de 25 centímetros. Com a parede a 8 centímetros do sensor, todos os retornos dela caíam abaixo desse piso, eram jogados fora como reflexo do próprio robô, o setor ficava vazio — e o código tratava setor vazio como caminho livre até o alcance máximo. Ou seja: quanto mais perto o obstáculo, mais convicto o nó ficava de que não havia nada. A proteção invertia exatamente onde precisa funcionar. O robô parou com a borda a 56 milímetros da parede enquanto o sistema anunciava 5,3 metros de folga.
Duas situações opostas estavam fundidas num único ramo: “nenhum eco” e “ecos, mas todos colados”. Separei. E fui verificar a premissa que justificava o filtro — a afirmação, escrita em comentário, de que fora do setor frontal o laser via a própria estrutura. Nunca tinha sido medida. Medi, com o robô em campo aberto: zero retornos em todo o arco de 180 graus, com o braço recolhido e estendido. O filtro inteiro protegia contra um fenômeno que não existe neste modelo.
O robô que anda depois que o programa morre
Ainda assim o robô ia para a frente e batia na chave, e desta vez depois do fim da missão. A conta não fechava com nenhuma fase.
Minha primeira hipótese foi que o controlador continuava perseguindo um alvo antigo. Antes de escrever a correção, instrumentei para medir — e a hipótese caiu: a missão não publica alvo cartesiano nenhum naquele modo, e o controlador fica em silêncio. O código que eu ia escrever seria morto, resolvendo um problema inexistente.
A causa real era mais simples. O plugin de tração do simulador não tem tempo-limite de comando: ele aplica a última velocidade recebida indefinidamente. O script de testes encerra a missão com um sinal que não permite desligamento ordenado, ela morreu no meio de um avanço, e o último comando continuou valendo. O robô rolou 42 centímetros até prensar a ferramenta na lâmina.
Escrevi um vigia que zera a base quando os comandos cessam. Reproduzindo o acidente de propósito: de 420 milímetros para 20. Vale para a bancada também — um programa que trava, um Ctrl-C ou uma queda de rede não devem deixar 28 quilos de robô rolando.
Quatro vieses, e um que era dois se cancelando
Sobrava a mira. A estimativa do olhal errava 45 milímetros em profundidade, sistematicamente, num furo que tolera 5. Decompus: o erro do offset geométrico era zero exato, então tudo vinha da estimativa da parede pela tag.
Foram quatro causas independentes. A placa da tag fica 22 milímetros à frente da parede e era tratada como se estivesse no plano dela. A posição era calculada com a rotação crua da estimativa visual, cujo erro de inclinação era multiplicado por um braço de quase um metro — o vínculo de que a parede é vertical já existia no código, mas era aplicado depois, quando o estrago já estava feito. A simulação publicava o centro óptico da câmera real enquanto o simulador renderiza centrado, um descasamento de sete pixels que dá exatamente o erro angular medido. E o detector enxerga a tag renderizada 1,4% menor do que ela é, por efeito de borda.
No meio da correção houve um momento que quase me enganou: depois de consertar duas causas, o erro de profundidade caiu para 2 milímetros e eu quase declarei resolvido. Era coincidência — dois erros de sinais opostos se cancelando. Ao corrigir só um deles, o erro foi para 16. Um resíduo pequeno não prova ausência de erro; prova ausência de erro líquido naquela condição.
Corrigidas as quatro, o viés foi de 45 milímetros para 2, e a dispersão vertical caiu de 2,9 para 0,6 milímetro.
O manual tinha razão, e eu não
Em algum ponto o orientador comentou que as juntas 4 e 5 do punho “se movem juntas”. Eu tratava as duas como independentes, e é assim que estão no modelo. Ele não tinha certeza da palavra e pediu que eu conferisse no manual.
Conferi — 286 páginas escaneadas, sem camada de texto, lidas como imagem. E ele estava certo, com um mecanismo mais específico do que “conectadas”: o motor do rolamento aciona a interface mecânica através de uma engrenagem cônica carregada pela carcaça do punho, e é a junta 4 que gira essa carcaça. Inclinar o punho rola a ferramenta mesmo com o motor do rolamento parado. Duas evidências independentes confirmam: o procedimento de referenciamento leva a junta 4 num passo e a 5 só no seguinte, e a manutenção pede graxa nas engrenagens do rolamento.
Achei também, no apêndice, que o rolamento tem duas representações de coordenada selecionadas por uma chave física na unidade de acionamento, lida apenas na energização — e o manual avisa que dados ensinados numa não valem na outra. É o tipo de armadilha que só aparece na bancada, no pior momento possível.
O painel, e o giro de cento e trinta e nove graus
Perto do fim, o orientador fez uma sugestão de método: a cada etapa da máquina de estados, mostrar a informação na simulação. O motivo é que ele diagnostica olhando a tela e eu diagnostico lendo registro depois do fato — e naquele dia ele acertou três vezes onde eu errei, sempre por isso. Escrevi um painel que desenha, ao vivo num terminal ao lado do simulador, o estado corrente, a fase, o erro contra a tolerância, as juntas pedidas contra as medidas e o ângulo da ferramenta.
O painel me obrigou a consertar um terceiro instrumento antes mesmo de usá-lo. As duas versões da cinemática que eu havia escrito devolvem grandezas diferentes no mesmo campo: uma devolve o ângulo entre a ferramenta e o eixo do furo, a outra devolve o erro total de atitude. Eu imprimia as duas com o mesmo rótulo — e foi assim que li oitenta e cinco graus de erro de atitude como se a ferramenta estivesse deitada de lado. Nomes diferentes para grandezas diferentes, e o painel passou a dizer qual está mostrando.
Na primeira execução com o painel na tela, o orientador viu o que nenhum número meu tinha mostrado: a ferramenta chega em frente ao anel, já apontada na direção certa, e então desce, contorna e sobe de novo passando por dentro do mecanismo. Ele perguntou por que aquele passeio existia.
Fui ao registro comparar a postura em que o robô termina o posicionamento com a postura que a primeira fase pede. O destino é praticamente o mesmo ponto no espaço. As juntas, não: o punho gira cento e trinta e nove graus e o rolamento cento e sessenta e quatro. O braço faz um arco enorme para chegar onde já estava.
A causa é que as duas etapas resolvem a cinemática com formulações diferentes, e formulações diferentes caem em ramos de solução diferentes — configurações distintas do braço que colocam a ferramenta no mesmo lugar. A continuidade que eu havia programado, para impedir exatamente esse tipo de salto, só valia dentro de uma fase; na fronteira entre etapas ela não atuava.
A segunda observação dele foi o mesmo defeito no outro extremo: para voltar ao ponto de partida, o robô deveria simplesmente retirar a ferramenta do anel e recuar. No caminho de sucesso existe uma fase que faz isso. No caminho de falha não existe nada disso — o braço vai direto de onde estiver para a posição recolhida, arrastando o que houver pelo caminho.
E havia ainda um defeito meu de projeto, que o painel deixou aritmético: a cinemática nova minimiza o erro de posição transversal ao furo, porque decidi de propósito que a posição ao longo do eixo é folgada dentro de uma fase. Só que a missão depois mede a distância completa contra a tolerância. A cinemática se declara satisfeita com dez milímetros enquanto a missão mede setenta e reprova. A fase não podia fechar — não por estar errada, mas porque as duas metades do sistema estavam medindo coisas diferentes com o mesmo nome. É o mesmo erro do rótulo, uma camada abaixo.
Onde parei, e por quê
O dia terminou com a ferramenta encostando no anel pela primeira vez, com o alinhamento resolvido e a mira vinte vezes melhor. Mas não terminou com a chave aberta por engate.
Nas últimas horas eu introduzi três regressões seguidas, e todas eram variações do mesmo erro: deixar dois candidatos plausíveis disputarem a cada passo em vez de decidir uma vez. Prioritizei alinhamento sobre alcance e o braço entrou na parede. Ordenei candidatos de um jeito que apagou o critério de desempate, e a cinemática passou a alternar entre duas soluções espelhadas, tentando girar cento e oitenta graus por dentro do mecanismo. Deixei a escolha entre duas formulações acontecer a cada iteração, e uma fase que fechava em oito milímetros passou a falhar com cinquenta e oito.
E, pior que as regressões, descobri que duas das minhas ferramentas de medição davam resposta errada. Uma filtrava os registros de contato linha a linha — e como cada contato ocupa duas linhas, os pares mistos entre ferramenta e chave perdiam metade e sumiam. Reportei “zero contatos” havendo milhares; o orientador viu a colisão na tela e me corrigiu. A outra julgava se a ferramenta entrou no furo medindo contra a posição do olhal fechado — só que o olhal anda junto com a lâmina quando ela gira, e o que eu li como “ferramenta longe do furo” era o olhal deslocado.
Houve ainda uma quarta regressão que eu atribuí à causa errada. Culpei uma mudança de postura que havia acabado de fazer, revertia-a com confiança e escrevi isso aqui — e a execução seguinte, já sem a mudança, falhou exatamente igual. A reversão não consertou nada, e a explicação que eu tinha dado ao orientador estava errada. Deixo o registro em vez de apagar: atribuir causa sem testar a atribuição é o mesmo erro de sempre, só que disfarçado de correção.
Parei por isso, e não por cansaço. O ciclo tinha ganho baixo e risco alto, e eu acabara de perder a confiança no instrumento — três vezes no mesmo dia. Deixei registrado no repositório o que ainda é chute meu e não medida — a profundidade do terminal fixo e o comprimento da lâmina — e o método correto de avaliar o engate.
O dia fecha com um saldo estranho de descrever. A percepção melhorou vinte vezes, a ferramenta chega alinhada em dois graus onde chegava a noventa, duas falhas de segurança reais foram encontradas e corrigidas, e a chave continua fechada. A régua vem antes da próxima tentativa — e a próxima tentativa começa por fazer as duas metades do sistema medirem a mesma coisa, e por impedir o braço de trocar de ramo entre uma etapa e outra.
26 Ago 2026
A Câmera que o Robô Não Tem — e Três Leituras Erradas de um Desenho
O dia começou pelo que faltava: a chave, na simulação, nunca tinha aberto de verdade. O robô encostava a ferramenta no lugar certo com precisão de milímetros e então uma chamada de serviço girava a peça — eu havia acoplado a abertura cinematicamente meses atrás, para destravar o resto do trabalho, e aquilo nunca fora revisitado. Ligar o contato real foi simples de escrever e o resultado foi imediato: as cinco fases fecharam com erro de 2,8 milímetros e a chave permaneceu fechada, em zero grau. A trajetória estava certa e não havia geometria alguma para prender o anel.
A causa era do mesmo tipo que já me consumiu dias neste projeto. A ferramenta tinha quatro cilindros desenhados formando um gancho, e uma única esfera como volume de colisão. Para os olhos, um gancho; para o motor de física, uma bola — e uma bola não engancha nada, empurra e escorrega. Corrigi dando colisão a cada trecho, e de quebra ao anel, que sofria do mesmo mal invertido: era um anel vazado no visual e uma esfera maciça na física, fechando justamente o furo por onde a ferramenta deveria passar.
Havia também um número que ninguém questionava. O atrito da junta da chave estava em 8 newton-metro. Como o ponto de aplicação fica a 20 centímetros do pivô, isso exige 40 newtons na ponta — muito acima do que o braço entrega, que é da ordem de 12. O valor tornava a tarefa fisicamente impossível, e ninguém percebia, porque o ângulo era imposto por fora. É o padrão que se repete: enquanto uma peça finge sucesso, os parâmetros à volta dela deixam de ser verificados.
A parte difícil do dia não foi essa. Foi entender como a ferramenta engata.
O orientador havia projetado uma peça para substituir os dedos da garra e me enviou o desenho técnico. Eu modelei a partir dele e, ao explicar minha interpretação, ele apontou que estava errada. Redesenhei. Errada de novo. Ele então mandou um esboço com a sequência de movimentos em cinco quadros, e eu li os quadros como vistas laterais quando eram vistas superiores — o que transforma “avança na horizontal” em “sobe na vertical” e inverte a mecânica inteira. Só na quarta tentativa fechou: a peça é um L visto de cima, com um dedo horizontal que atravessa o anel movendo-se paralelo à parede; o anel então repousa sobre esse dedo por gravidade, e é isso que o prende. Não há aperto nenhum.
O que destravou não foi eu pensar melhor, foi ele sugerir que eu desenhasse minhas dúvidas em vez de descrevê-las. Cada desenho meu tornava explícito um pressuposto que o texto escondia, e ele podia apontar o erro em vez de reformular a explicação. Levei três rodadas de desenho para chegar onde nenhuma quantidade de perguntas em prosa teria chegado. Ficou como método.
Essa compreensão trouxe uma consequência boa e uma ruim. A boa: como o dedo apenas sustenta o anel por baixo, a ferramenta não precisa perseguir a trajetória de abertura com precisão — basta não descer menos que o anel nem recuar mais rápido do que ele se afasta. É uma restrição bem mais frouxa do que eu supunha. A ruim: a fase em que a ferramenta atravessa o anel exige um deslocamento lateral de nove centímetros, e o braço, já esticado para a frente, não tem alcance nessa direção. A fase estacionou com dois centímetros e meio de erro sem convergir.
E então, perto do fim do dia, veio a observação que reordenou tudo: ainda estávamos usando uma câmera que o robô não tem.
Meses atrás, para fechar o laço de percepção na simulação, eu acrescentei ao modelo uma câmera no punho. Ela nunca existiu no robô real, que tem apenas a câmera de rastreamento — cujas lentes são olho-de-peixe e estavam desligadas no arquivo de configuração. Todos os números de percepção que venho registrando aqui — o alcance de detecção, o ponto cego na posição de trabalho, a taxa de acerto da busca — foram medidos com um sensor imaginário.
Troquei a simulação para usar a lente olho-de-peixe real, com o modelo de projeção correto e publicando nos mesmos canais que o driver do sensor físico. E o problema inverteu de lado: a nova câmera enxerga a tag na posição de trabalho, onde a anterior era cega, e não enxerga a três metros, onde a busca acontece hoje. Ganha campo de visão, perde resolução angular. A busca vai ter que se aproximar procurando, varrendo com as juntas do braço, em vez de girar no lugar de longe.
Descobri também que o simulador renderiza com a lente correta mas informa parâmetros de uma lente comum, com uma distância focal quase quatro vezes menor que a verdadeira — o que faz a estimativa de distância errar por um fator equivalente. Corrigi publicando os parâmetros certos. Sobrou um erro de escala que ainda não expliquei: a estimativa erra proporcionalmente à distância, meio metro a um metro e trinta, e acerta apenas quando o robô está colado na parede.
Parei aí de propósito. Escrever a nova rotina de busca antes de resolver esse erro seria calibrá-la contra um alcance medido errado — exatamente o tipo de trabalho que hoje já me obrigou a desfazer três vezes.
E então fomos para a bancada medir de verdade, o que mudou quase tudo que eu havia registrado.
A primeira surpresa foi minha. O sistema listava a câmera de rastreamento sob um nome de outro produto, e eu concluí que ela estava travada num modo de recuperação — mandei o orientador trocar a porta duas vezes, sugeri problema de cabo, cogitei regravar o firmware. Bastava rodar o utilitário do próprio fabricante, instalado o tempo todo, para ver que a câmera estava perfeita: nome, número de série, versão de firmware e modos de operação, todos corretos. Aquele nome estranho é simplesmente o processador que ela usa. Foi a terceira vez no mesmo dia que deduzi de sintoma genérico quando havia uma fonte específica a um comando de distância — e a segunda em que isso custou uma intervenção física de outra pessoa.
Com a câmera reconhecida, as medidas vieram rápido e foram corrigindo minhas suposições uma a uma. O modelo de lente que eu havia assumido estava certo, mas a distância focal que eu calculara errava por quase nove por cento, o centro óptico não é o centro do sensor, e a lente tem coeficientes de distorção que eu havia zerado. O campo de visão real é treze graus mais aberto que a especificação sugeria. E há um detalhe que passa despercebido no papel: a câmera usada para ver não está no centro do sensor de pose — fica três centímetros de lado, e o modelo assumia que estavam no mesmo ponto.
Depois medimos alcance, com a tag impressa e uma trena, afastando em passos. O resultado desmontou o critério que eu vinha usando. A detecção sobrevive bem além do que eu previa — a um metro e oitenta ainda acerta todos os quadros —, mas a precisão degrada muito antes: já a um metro e meio a estimativa oscila centímetros entre quadros. Eu vinha medindo a coisa errada: o limite útil não é onde o marcador deixa de ser detectado, é onde a medida deixa de servir para posicionar um robô.
O colapso, quando vem, é abrupto. Entre dezesseis e catorze pixels de lado do marcador, a taxa de acerto cai de sessenta por cento para dois. São dois pixels. Faz sentido: o marcador tem oito módulos no lado, e abaixo de dois pixels por módulo o decodificador não consegue amostrar o padrão.
Nesse meio houve mais uma lição de método, dentro da própria medição. A primeira leitura a dois metros e vinte deu cinco por cento de detecção, e eu quase a registrei — mas ela fora capturada enquanto a câmera ainda se movia, e o borrão derrubava o detector. Parada, a mesma distância dá sessenta por cento. Medi durante o transiente e quase publiquei o número errado.
O saldo é que agora existe uma caracterização de percepção do robô feita contra o hardware, com número e trena, substituindo uma tabela inteira que fora medida num sensor imaginário. E ela condena o dimensionamento atual da missão: a busca acontece a quase três metros, e com a tag que temos impressa a detecção confiável termina antes de dois.
25 Ago 2026
Quem Puxa a Chave São as Rodas — e Três Correções para Voltar ao Mesmo Lugar
O dia começou com uma dívida: a missão fechava em 5 de 8 tentativas, e eu queria entender as 3 que falhavam. Terminou em 23 de 24 — mas o caminho entre os dois números diz mais sobre método do que sobre robótica.
As três falhas apontavam todas para a mesma linha: uma postura do braço que não era atingida no tempo limite. Fui atrás e encontrei, no log, erros de posição de junta que pareciam absurdos — mais de 100 graus. Só que o estimador, medido com o braço parado, erra 0,2 grau. A contradição se explicou quando fui ver o outro log: a cinemática inversa do estimador nem sempre converge, e nessas horas devolve uma postura sem sentido. Eu vinha consumindo essas leituras como se fossem boas, porque o canal que eu assinava não carrega a informação de convergência. Corrigi para usar o canal que carrega.
A correção não melhorou nada. A taxa caiu de 5 para 3 em 8. Tentei outra coisa — deixar a medida corrigir sem bloquear a conclusão da postura — e caiu para 2 em 8. Foi aí que instrumentei o erro por junta, em vez de olhar só o maior, e a resposta apareceu: o resíduo persistente estava sempre no ombro, entre 9 e 12 graus, e não cedia. Aquilo não era divergência, era o erro estacionário normal do controlador de posição sob carga — o braço faz isso o tempo todo, e a missão já tinha sido projetada para absorver, medindo onde a ponta da ferramenta realmente foi parar e re-mirando. Eu havia imposto ao sistema um requisito que ele não precisava atender.
E havia um defeito estrutural pior, meu, do dia anterior. A correção que eu tinha escrito empurra o comando além do alvo, de propósito, para vencer justamente esse erro estacionário. Mas o teste de “cheguei” comparava a distância até o alvo — que volta a crescer por causa do empurrão. O código então caía de volta no modo de aproximação, que desfazia a correção. Dois trechos do mesmo controlador se anulando em ciclo, indefinidamente. Corrigido, a taxa voltou para 5 em 8.
Três correções seguidas para retornar ao ponto de partida. As duas primeiras são defensáveis em si — ninguém deveria consumir uma estimativa que não convergiu —, mas nenhuma moveu o resultado. O que consumiu as horas foi desfazer um problema que eu mesmo havia criado ao consertar outro sem medir antes de seguir adiante.
A mudança que funcionou veio do orientador, e não era de software. Observando a simulação, ele notou que o robô parava perto demais da parede e que o movimento de puxar o olhal esbarrava nela. A proposta: separar em dois momentos — o braço se arma numa posição mais afastada, e só depois o robô avança para engatar — e, mais importante, fazer o puxão recuando o chassi em vez de recolher o braço.
A segunda parte é a que muda o problema. A abertura afasta o olhal da parede em 8,5 centímetros. Pedir isso ao braço significa movê-lo justamente na região apertada; pedir às rodas significa que a postura do braço fica parada e quem se desloca é a plataforma inteira, que tem tração de sobra e nenhuma restrição de alcance ali. Ao braço sobra apenas a descida vertical, onde ele tem folga.
O resultado foi mais claro do que eu esperava. 23 de 24 execuções completas, com a chave aberta em 24 de 24. As duas fases que passaram a ser feitas pelas rodas viraram as mais precisas de todas: 5,4 e 6,1 milímetros de erro médio, contra 10,2 da fase de engate, que continua a cargo do braço. E as rodas se mostraram um atuador melhor do que eu supunha nessa escala — erro médio de 1,5 milímetro ao longo de 48 puxões.
Rodei 24 execuções em vez das 8 habituais de propósito. Com 8 amostras, 5 acertos e 7 acertos são indistinguíveis estatisticamente, e eu já havia tomado decisões erradas neste projeto confiando em diferenças desse tamanho. Com 24, o intervalo de confiança da nova taxa vai de 80% a 99%, e a taxa antiga de 62,5% fica fora dele. É a diferença entre afirmar que melhorou e achar que melhorou.
Fica em aberto o tombamento, que ficou raro — 1 em 24 — mas não foi resolvido, e cuja causa eu ainda não sei.
24 Ago 2026
Quatro Peças que Relatavam Sucesso — e a Repetibilidade que Faltava
Na sessão anterior a missão fechou inteira e eu comemorei. Faltava a pergunta desconfortável: ela fecha de novo? Rodei uma bateria de oito execuções idênticas e a resposta foi 1 em 8. Uma missão que funciona uma vez não é um resultado, é uma anedota — e o dia inteiro foi gasto descobrindo por que as outras sete falhavam.
O que apareceu tem um padrão que vale mais que os bugs em si. Quatro componentes independentes estavam relatando sucesso enquanto erravam. Nenhum deles quebrava, travava ou lançava exceção. Cada um respondia “concluído” com convicção, e o sistema seguia em frente sobre uma premissa falsa. Foi por isso que custaram tanto a aparecer.
O mais sutil estava na medida de erro de orientação. Meu código calculava o quanto duas orientações diferem usando a parte vetorial da matriz de rotação — uma fórmula comum, que dá sen(θ) vezes o eixo de giro. Para desalinhamentos pequenos ela funciona bem. Mas o seno cresce até 90 graus e decresce depois, voltando a zero em 180. Ou seja: uma orientação completamente invertida era relatada como erro exatamente zero, indistinguível de alinhamento perfeito. E, pior que o ponto cego, o gradiente acima de 90 graus apontava para os 180 — não era uma zona de silêncio, era um atrator estável.
A consequência era grave e silenciosa. O estimador de postura do braço, que infere os ângulos das juntas a partir da câmera de rastreamento (o robô não tem encoders), convergia declarando sucesso com a posição da câmera certa a menos de um milímetro e a orientação virada de cabeça para baixo. Como a ponta da ferramenta fica 22 centímetros fora do centro dessa câmera, ela ia parar 45 centímetros longe do real. Toda a manipulação, que é baseada nessa ponta, perseguia um alvo fantasma; o controlador estendia o braço para corrigir um erro que não existia, e o robô tombava. Trocada a fórmula pelo logaritmo em SO(3), que devolve o ângulo verdadeiro, o erro da ponta caiu de 45 centímetros para 2 milímetros.
O segundo travava a missão de forma mais direta. A ponte que comanda o braço integra internamente a posição das juntas e só lia os valores reais uma única vez, na inicialização — algo fiel ao robô físico, que não tem encoders, mas que torna o estado interno impossível de corrigir depois. Ela declarava “postura atingida” comparando o alvo com o próprio modelo. Quando modelo e realidade divergiam, ela anunciava chegada com o braço 28 graus fora do lugar e, por achar que tinha chegado, parava de acionar. Cada nova tentativa da missão era comparada com o mesmo modelo, que já dizia estar no alvo, e recebia “chegou” instantaneamente. A ponta ficava cravada e as cinco iterações da fase davam erro idêntico.
O que me fez perder horas nesse foi que todas as peças isoladas funcionavam. Medi a posição da ponta contra a verdade do simulador: exata em 0,1 milímetro. Comandei a mesma postura fora da missão: chegava com zero grau de folga. Verifiquei alcance, colisão com a parede, distância — tudo certo. O defeito só existia na composição, e só apareceu quando comparei, lado a lado, o que a ponte anunciava com o que a missão media.
Os outros dois eram de infraestrutura de teste, e por isso mesmo perigosos. Meu próprio verificador de reinicialização aprovava o estado errado: checava se o robô estava nivelado, mas não se o braço estava na postura de repouso. Aprovou uma partida com a junta do punho no batente, e a medição seguinte já nasceu contaminada. E eu vinha conferindo a integridade do sistema com um comando que lista registros de nós no mestre ROS, não processos vivos — um nó que morre de forma abrupta deixa o nome registrado para trás. Uma bateria inteira, oito execuções e cerca de uma hora, rodou sem o estimador de postura enquanto o sistema parecia íntegro. Na faxina que se seguiu encontrei 215 processos órfãos de sessões anteriores ainda vivos na máquina, publicando nos mesmos canais.
Com a ponte corrigida, a mesma bateria de oito passou de 1 para 5 execuções completas, com a chave aberta em 6 delas. A precisão da manipulação ficou entre 5,5 e 10,4 milímetros em todas as trinta fases bem-sucedidas, cada uma fechando numa única iteração. Três execuções ainda falham, por causas que ainda não diagnostiquei — e prefiro registrar isso do que arriscar um palpite, porque hoje mesmo tive três hipóteses minhas refutadas por medição: o ramo de solução da cinemática, o contato da ferramenta com a parede e o laser enxergando o próprio braço. Todas pareciam encaixar nos dados até eu medir.
Houve ainda uma descoberta que muda o discurso do projeto, não só o código. Instrumentei uma missão inteira a 50 Hz e o controlador whole-body fuzzy — que é a contribuição central da tese — não comandou velocidade de junta uma única vez, em mais de 21 mil amostras. O caminho que o ativa existia, estava documentado, e nunca era chamado. Ele havia sido desligado numa sessão anterior para contornar um conflito com a cinemática inversa, e o desligamento ficou. Toda a precisão milimétrica que venho medindo é mérito da IK iterativa, não do controle whole-body. Religuei o caminho e ele passou a atuar, mas deixei desligado por padrão: nas execuções feitas até agora ele não melhorou a missão, e ligar por padrão um caminho não validado é trocar um problema conhecido por um desconhecido.
21 Ago 2026
A Missão Completa Fecha — e Três Bugs que Não Eram o que Pareciam
A missão que eu vinha perseguindo há dias finalmente rodou inteira: o robô procura a AprilTag girando no próprio eixo, navega até uma pose de aproximação segura, remede a chave de perto, estende o braço, engata a ferramenta no olhal, executa o movimento de abertura em duas etapas e volta ao ponto de partida. Sete estados de máquina, do início ao fim, terminando em MISSION_OK. O caminho até aqui foi menos sobre escrever controle novo e mais sobre descobrir que quase tudo que eu achava que estava errado, não estava.
O primeiro engano foi meu diagnóstico favorito: droop. A manipulação fina estacionava a alguns centímetros do alvo, e minha explicação era que os controladores de posição das juntas não venciam a gravidade — o braço ficaria sistematicamente aquém do comandado. Media bonito na teoria. Quando finalmente instrumentei e medi de verdade, o droop nas posturas de trabalho era de 0,2 a 0,8 grau, o que dá poucos milímetros na ponta da ferramenta. Não explicava nada. A hipótese que eu carregava havia sessões inteiras simplesmente não tinha sustentação nos dados.
O segundo engano foi mais caro, porque eu cheguei a anunciar sucesso. Ao mover a tag de referência para o outro lado da chave, o orientador pediu atenção às direções no código. Eu conferi o que achei relevante e rodei: a missão executou inteira e terminou em MISSION_OK. Fui verificar os números por hábito e a estimativa da parede estava 79,5 centímetros fora do lugar — quase exatamente o quanto a tag havia se deslocado. O robô tinha executado toda a tarefa com precisão milimétrica em cima de uma chave que não estava ali. A causa era uma constante de geometria duplicada: o nó de percepção mantinha a própria cópia do offset da tag, e eu havia atualizado só a outra. Existe um módulo no projeto criado justamente para evitar essa duplicação, e a cópia estava lá desde antes dele. A correção foi de uma linha; a lição é que sucesso reportado por uma máquina de estados só vale o quanto valem as medidas que alimentam ela.
O terceiro engano foi o mais teimoso. A fase de engate nunca fechava: a ponta parava alguns centímetros aquém, e cada tentativa de compensar piorava. Passei por várias explicações plausíveis — condicionamento da Jacobiana, escolha de ramo da cinemática inversa, ganho do controlador — e implementei correções reais para cada uma. Nenhuma resolveu. O que resolveu foi instrumentar o óbvio: registrar, a cada iteração, o ângulo que a cinemática pediu e o ângulo que a junta efetivamente alcançou. O punho ficava cravado num valor, e a defasagem crescia a cada tentativa. Um teste controlado fechou o caso — comandar exatamente a mesma postura com o robô longe da parede, onde a junta chegava com folga, e depois na posição de trabalho, onde não chegava. Era colisão.
E a colisão era invenção minha. O olhal da chave — o anel onde a ferramenta engata — eu havia modelado visualmente como um anel vazado, montado com vários cilindros em círculo. Mas, para o motor de física, eu havia declarado o volume de colisão como uma esfera maciça. Visualmente era um anel; fisicamente era uma bola. O gancho batia nela em vez de atravessar, exatamente onde deveria engatar. Removida essa colisão, a lâmina logo atrás bloqueava pelo mesmo motivo. E, na raiz de tudo, a geometria estava apertada demais: eu havia modelado o mecanismo a 3 centímetros da placa de fundo, com o suporte ocupando quase esse espaço inteiro — não havia por onde um gancho entrar. Aumentei o afastamento para 8 centímetros, o que resolveu o problema e, de quebra, ficou mais fiel à foto da bancada real, onde o mecanismo se projeta bem da placa.
Fechada a manipulação, a estratégia que funcionou foi trocar o servo cartesiano contínuo por cinemática inversa iterativa: resolver a IK para o alvo, comandar a postura, medir onde a ponta realmente foi parar, e recomandar mirando além na medida do que faltou. Fecha em uma ou duas iterações, com erro final entre 3 e 16 milímetros. Um detalhe importante foi desligar o controlador cartesiano durante essa fase — com ele ativo, ele seguia perseguindo o alvo antigo e movia a base, tirando o robô justamente do lugar para o qual a IK tinha sido resolvida.
O dia rendeu também três sensores que estavam no robô e não estavam sendo usados, todos apontados pelo orientador ao olhar a simulação. A IMU existia no modelo apenas como geometria, sem sensor e sem consumidor — o robô havia tombado várias vezes com um sensor de atitude a bordo, mudo. Agora publica inclinação e aborta a missão automaticamente; na primeira execução após ser ligada, ela detectou um tombamento e encerrou a tarefa com segurança, algo que antes passava despercebido enquanto a máquina de estados seguia tentando. O laser Hokuyo publicava desde sempre e ninguém assinava: eu vinha contendo a aproximação da parede por um plano calculado a partir da tag, ou seja, por inferência, quando havia medida direta disponível. E não havia como visualizar o que a câmera enxergava — toda verificação era uma captura de tela sob demanda; agora há uma imagem anotada publicada continuamente, que no primeiro uso já revelou a câmera apontada para o próprio braço do robô.
20 Ago 2026
A Semana em que a Chave Abriu: a Trava que Saturava o Braço e Três Sensores que Estavam Mudos (13 a 20 Ago, retroativa)
Entrada escrita em 08 de outubro, reconstruída a partir dos commits e das anotações da época. A semana entre o tombo de 12 de agosto e a missão completa de 21 de agosto tinha ficado sem diário, e foi nela que a chave abriu pela primeira vez na simulação.
13 de agosto: a missão ganha uma máquina de estados e o braço aprende a se recolher
O dia começou validando o AprilTag ao vivo no Gazebo, aplicando a lição do tombo da véspera: controlador Fuzzy morto antes de posicionar e pose da base por cinemática direta. A tag entrou no quadro na primeira tentativa, e três erros previstos como risco apareceram de uma vez: o frame óptico do OpenCV aponta Z para a frente e o do link aponta X, a zona branca em volta da tag fazia o PnP superestimar a distância em um quarto, e a rotação entre a tag e a fixture tinha dois sinais trocados. Corrigidos, a parede saiu a dezoito milímetros a noventa centímetros e a quarenta e dois a um metro e vinte, degradando com a tag pequena na imagem, como na validação com a câmera real.
O orientador pediu então duas coisas. A primeira, uma postura recolhida: o eixo dois para cima e o três para baixo, o braço dobrado junto do centro de gravidade, porque o braço esticado tinha tombado o robô duas vezes no dia anterior. A postura nasceu com os dois eixos no limite do modelo, e com ela a ponta da ferramenta passou de noventa e quatro para trinta e cinco centímetros à frente. Foi essa postura dobrada que expôs um erro de fundo: o Jacobiano do estimador derivava a orientação absoluta enquanto o erro minimizado era a rotação relativa, e em poses estendidas isso passava despercebido desde a Fase 2. Na postura recolhida o estimador travava num mínimo espelhado, com o ombro estimado em dezoito graus e o real em sessenta e um. O Jacobiano passou a derivar o próprio erro, com passo limitado, e o estimador fechou a meio grau por junta.
A segunda, a missão inteira: procurar a chave pela tag, aproximar, simular a abertura e voltar ao ponto de partida. Nasceu a máquina de estados em SMACH, sete estados e um estado terminal de abandono que para a base e recolhe o braço, para nunca deixar o robô parado na postura estendida. A busca gira no próprio eixo até a tag aparecer, sem nenhum atalho pela verdade do simulador, de propósito. Ficou escrito como não testado de ponta a ponta, porque a fixture vivia em outra branch.
17 de agosto: a chave que não abria porque não tinha junta
Um dia inteiro com o orientador apontando o que a simulação mostrava. A chave estava rotacionada noventa graus, inclinava de lado em vez de para fora da parede; o pivô estava em cima e o olhal embaixo, o contrário da bancada; a lâmina inclinada era a posição aberta, não a fechada; o olhal era uma barra e precisava ser um anel, porque é por dentro dele que o gancho passa; e a ferramenta terminava num disco chato que não engancharia nada. Cinco correções de geometria, uma atrás da outra, todas conferidas no URDF processado para o olhal não sair do lugar que a percepção já conhecia.
E a mais simples de todas: a chave não abria quando o robô puxava porque não tinha junta nenhuma. A fixture inteira era rígida e o modelo era estático, e modelo estático no Gazebo nem simula juntas. A lâmina ganhou uma junta revoluta de zero a trinta graus, com atrito alto, e a missão passou a girá-la acompanhando os waypoints. O acoplamento ficou cinemático, não por contato, e isso ficou documentado como escolha: puxar um anel com um gancho é contato fino entre superfícies curvas, e o motor de física com colisões primitivas não resolve isso de forma confiável. A versão por contato viria semanas depois, com o gatilho.
No controle, três achados encadeados. A tolerância de cinco milímetros contra um alvo medido com nove de incerteza era perseguir ruído, e foi para vinte. O controlador Cartesiano encalhava as juntas nos batentes ao tentar vencer dezoito centímetros de uma vez, e cada waypoint passou a ser alcançado por cinemática inversa em espaço de juntas, com o Cartesiano fechando só o resíduo local. E o gimbal que mantinha a tag centrada durante a navegação deixava uma rampa de postura pendente que sequestrava a ponte do braço na hora da manipulação. Entrou também o vetor de aproximação: um ponto a dezoito centímetros do olhal antes de entrar reto, porque o controlador não tem noção de obstáculo e o caminho que ele escolhia passava por dentro da parede.
A noite fechou com uma hipótese minha refutada pela medição. Eu apostava que a ponte do braço perdia comandos; instrumentada, ela recebia e aplicava tudo. O que a medição mostrou foi o ombro indo a sessenta e cinco graus e o cotovelo a sessenta, os dois no batente, com o erro congelado em quase dez centímetros. A causa era a trava de base que eu tinha posto como segurança contra a parede: com a base travada o braço tem só cinco juntas para a tarefa e, naquela configuração, falta autoridade na direção necessária. O orientador tinha visto isso na tela antes de eu medir: o Pioneer podia atuar de forma mais otimizada para abrir a chave. Esse é exatamente o motivo de existir o controle de corpo inteiro. O MoveIt, cogitado, foi descartado na mesma noite: pressupõe realimentação de junta, que o braço não tem.
19 e 20 de agosto: a base volta ao jogo e três sensores saem do silêncio
A trava de base virou uma restrição de aproximação: a base participa dos oito graus de liberdade e só perde a liberdade de avançar além de um plano de exclusão, publicado pela missão depois de remedir a chave. O DLS puro, porém, distribuía o movimento pela norma mínima e levava o chassi de oitenta para cinquenta e três centímetros da parede, porque mover a base custava menos do que mover o braço. Uma ponderação doze vezes maior sobre a base fez o braço cuidar do fino. Numa execução a missão encadeou quatro fases e passou do engate pela primeira vez. Não era repetível, e o suspeito ficou anotado: o resíduo do controlador de posição após o movimento em espaço de juntas, que a malha Cartesiana às vezes fechava e às vezes não.
O orientador então levantou quatro pontos sobre recursos que o robô tinha e a simulação não usava. Não dava para ver se a câmera encontrava a tag: entrou a imagem anotada com os marcadores, os eixos do PnP e a faixa confiável em verde ou vermelho, e no primeiro uso ela mostrou a câmera apontada para o próprio braço. A IMU existia no modelo só como geometria, sem sensor, sem tópico e sem consumidor, e o robô tinha tombado várias vezes com ela muda: ganhou sensor, um monitor de inclinação e a missão passou a abortar em inclinação crítica. O Hokuyo publicava desde sempre e ninguém assinava: entrou um nó que mede a folga livre à frente, calibrado a um centímetro e meio contra a verdade do simulador, e a navegação passou a frear por medida direta em vez de por um plano inferido da tag.
A primeira versão do freio pelo laser tinha um erro de frame: eu bloqueava a primeira componente da velocidade da base achando que era o avanço, mas ela vem no frame do mundo, e com o robô encarando a parede no eixo Y a checagem nunca disparava. O robô foi de oitenta para trinta e um centímetros da parede e tombou. Dessa vez, porém, a IMU recém-ligada detectou a queda e abortou a missão: a primeira vez que o robô cai e o sistema percebe sozinho. Com a velocidade projetada no rumo, o robô ficou em pé, parado a oitenta e nove centímetros, sem o laser precisar intervir. O engate ainda não fechava, estacionando a três centímetros e meio. Foi o estado em que a semana terminou, e a entrada de 21 de agosto conta como a missão fechou inteira no dia seguinte.
O que fica
Dezessete commits em quatro dias, quase todos nascidos de uma observação do orientador diante da tela: a chave de lado, o pivô invertido, o braço que batia na parede, o Pioneer que podia ajudar, a câmera que ninguém via, a IMU e o laser mudos. A lição que atravessa a semana é a mesma que reapareceria em outubro, sobre o estimador: a simulação só serve quando usa o que o robô tem, e só o que o robô tem.
12 Ago 2026
A Fixture Encontra o Robô de Verdade (e Ele Tomba Duas Vezes)
A fixture da chave seccionadora, construída na sessão de 08 de agosto, nunca tinha sido testada contra o robô em movimento — só validada visualmente, parada, com o modelo já correto na cena. Marco compartilhou um documento novo descrevendo o movimento de abertura em si (um diagrama técnico com eixos e um arco de posições, mais uma foto da bancada física) e a primeira correção que ele trouxe já invalidava parte do que eu tinha construído: a bancada real não tem isolador — só a chave direto na parede — e o curso de abertura é de 30° de giro, com o olhal descendo, não subindo, o oposto do que a geometria de pivô do xacro sugeria sozinha. Removido o isolador, recalculada a altura de montagem, verificado numericamente que o olhal continuava caindo exatamente nos 810 mm especificados, e a sequência de tarefa (engatar → liberar → abrir em duas fases) virou um nó novo, task_sequencer.py, publicando os alvos em sequência para o controlador whole-body.
Rodar esse nó pela primeira vez de verdade no Gazebo — não só ler o código, subir o stack inteiro e ver o que acontece — encontrou dois bugs que nenhuma revisão de código teria pego. O primeiro: /gazebo/get_link_state simplesmente não resolve o link do olhal, porque toda a fixture (parede, chave, tag) é uma cadeia de juntas fixas, e o Gazebo funde cadeias assim num único corpo rígido na conversão para o formato de física — só o corpo raiz da fusão (wall_link) sobrevive como algo consultável. O segundo: o mesmo serviço só entende reference_frame: 'world', não 'odom', o nome que o resto do stack usa para exatamente o mesmo referencial — uma incompatibilidade de nome, não de física. Os dois só apareceram porque o nó de verdade tentou consultar o simulador e falhou; nenhuma leitura estática do xacro ou do Python teria revelado nenhum dos dois.
Corrigidos os bugs, o robô finalmente tentou se aproximar da chave — e bateu na parede, tombando. A causa não era sutil, mas exigia ver o robô se movendo pra perceber: a parede estava deslocada 3 metros no eixo X do mundo, mas com a normal da sua própria geometria apontando no eixo Y — o robô, andando reto a partir da origem, esbarrava na borda fina da parede em vez de se aproximar de frente da face onde a chave está montada. Corrigido o eixo de spawn (Y, não X, mesma rotação de antes), a face larga passou a confrontar o caminho do robô como deveria — confirmado visualmente, comparando a nova captura de tela com a anterior.
O robô tombou de novo mesmo assim. Dessa vez a causa era de alcance: o braço RV-M2 sozinho tem só uns 50-70 cm de reach, insuficiente pra deixar a base parada a uma distância segura da parede — ela precisava chegar praticamente encostada pra o efetuador alcançar o olhal, e “praticamente encostada” bastou pra derrubar o chassi inteiro no contato. A solução, que Marco confirmou como a direção certa, foi adicionar uma ferramenta real no efetuador: uma vara rígida de ~35 cm com um gancho na ponta, a mesma lógica de uma vara de manobra de eletricista de verdade — manter distância de um ponto energizado. Isso exigiu uma peça nova de matemática: como o T265 continua sendo o único sensor de pose do efetuador (arquitetura sem encoder de junta), qualquer alvo pensado para a ponta da ferramenta precisa ser convertido num alvo equivalente pro T265 antes de ser publicado — uma transformação fixa e conhecida entre os dois pontos, calculada pela mesma cadeia cinemática do xacro.
Terceira tentativa, terceiro problema — esse mais interessante que os dois anteriores. A ferramenta existia e aparecia certinha no Gazebo, mas o robô ainda chegou perto demais da parede. A causa: o código mantinha fixa qualquer orientação que o efetuador tivesse no instante em que a tarefa começava — um detalhe que parecia inofensivo antes de existir uma ferramenta apontando para algum lugar específico. Na prática, essa orientação era arbitrária, e o offset de 35 cm da vara acabou quase inteiro “gasto” nos eixos errados (apenas 1 mm de diferença no eixo que realmente importava, o perpendicular à parede) — a ferramenta existia, mas não estava de fato apontando para o alvo, então não deu folga nenhuma de verdade.
Foi nesse ponto que Marco identificou o problema real, por trás de todos os sintomas anteriores: o robô não estava fazendo nenhuma leitura do ambiente antes de se mover. O alvo Cartesiano vinha direto de uma consulta de verdade-absoluta ao simulador (get_link_state) — uma trapaça que nenhum robô real teria, e que escondia a ausência completa de qualquer noção de obstáculo no caminho. A pergunta seguinte foi direta: por que não usar a própria tag AprilTag, já fixada acima da chave, pra calcular isso? Descobri que o OpenCV já instalado tem suporte nativo ao dicionário AprilTag 36h11 (cv2.aruco), sem precisar instalar apriltag_ros nem o pacote apriltag — bastou adicionar uma câmera de tarefa no punho do robô e escrever um nó que detecta a tag, resolve a pose por PnP, e combina com a pose do T265 pra saber onde a parede está no mundo, substituindo a consulta direta ao simulador por um pipeline de percepção de verdade.
O pipeline compila e roda, mas a sessão terminou antes de uma detecção ao vivo confirmar os números. Posicionar a câmera — montada no punho, então sua direção depende da configuração das cinco juntas do braço de um jeito nada óbvio — dentro do campo de visão da tag, de forma controlada pra calibrar, provou ser mais fiddly do que parecia à primeira vista, e tentativas de posicionamento manual foram sabotadas pelo controlador whole-body, que continuava rodando e brigando de volta pro alvo antigo. Documentado explicitamente no código como não validado, não escondido atrás de um “deveria funcionar” — a validação ao vivo fica para a próxima sessão.
08 Ago 2026
Uma Chave Seccionadora de Verdade na Simulação
Com o braço confirmado saudável na sessão anterior, o próximo passo natural era dar à simulação um alvo de verdade para a tarefa que motiva toda essa pesquisa: abrir uma chave seccionadora. Não um cubo genérico de placeholder — uma chave seccionadora unipolar real, base “C”, tipo “LF”, com as dimensões exatas de um datasheet comercial (STIeletrônica), o olhal de engate a 810 mm do chão e uma tag AprilTag de referência visual a 1030 mm, ambos números vindos de especificação direta, não de estimativa.
A geometria em si foi construída de baixo para cima a partir de um único ponto fixo: o olhal, o anel onde a vara de manobra (ou a garra do robô) engata para abrir a chave. Toda a cadeia acima dele — lâmina, isolador, terminal, suporte — foi montada por trigonometria a partir do ângulo de 20° que o próprio datasheet documenta para a posição fechada, de forma que o olhal caísse exatamente em 0,810 m, não aproximadamente. Verifiquei isso duas vezes: uma vez implicitamente no xacro, e uma segunda vez com um script Python independente que recalcula a mesma cadeia de transformações do zero — 0,8100 m, sem arredondamento generoso escondendo erro.
O pedido seguinte mudou o desenho pela metade: em vez de dois postes soltos (um para a chave, um para a tag), uma parede única segurando os dois — e a parede a 3 metros do ponto de spawn do robô, não colada nele. Essa segunda parte importa mais do que parece: um alvo a 55 cm do braço testa só a cinemática do manipulador; um alvo a 3 metros testa a tarefa inteira, navegar e só depois manipular, que é o problema real da tese.
O resultado visual, porém, saiu cinza-chumbo uniforme na primeira tentativa — nenhuma cor das que eu tinha definido apareceu, nem o branco do isolador, nem o cobre do terminal. A causa não estava na definição de material em si, mas numa característica pouco documentada do Gazebo: quando uma cadeia de links é unida só por juntas fixas (como é o caso de um objeto estático inteiro), o conversor URDF→SDF funde todos eles num único corpo rígido por eficiência de física — e nessa fusão, o material de cada <visual> individual pode ser descartado, sobrando só um cinza padrão. A correção veio de declarar o material explicitamente por fora do URDF, direto no bloco <gazebo reference="...">, que sobrevive à fusão porque é aplicado depois dela, não antes.
Corrigido o material, sobrava a textura da tag, que insistia em aparecer como um quadrado cinza liso em vez do padrão AprilTag 36h11. O log interno do OGRE (o motor de renderização do Gazebo) revelou o motivo exato: a convenção de pasta que o plugin de descoberta de recursos escaneia automaticamente é media/materials/scripts/, com um “media” extra no meio — não materials/scripts/ direto, que é onde eu tinha colocado o arquivo. Um nível de pasta errado, silencioso, sem nenhum erro no log principal — só visível remontando a estrutura real que o Gazebo registrou ao subir.
Com os dois bugs resolvidos, a fixture ficou de pé: parede, chave nas cores certas, tag renderizando o padrão certo, tudo a 3 metros do robô e voltado para ele. Testei girando o modelo algumas vezes até descobrir, por tentativa e erro, que minha conta trigonométrica original para a orientação de spawn estava invertida — o valor que funciona de verdade é diferente do que a matemática “deveria” dar, um lembrete de que vale a pena confirmar visualmente mesmo quando a álgebra parece impecável no papel.
07 Ago 2026
A Falha Elétrica que Nunca Existiu: ModemManager e o Firmware Errado
A manhã foi dedicada ao artigo da tese — Introdução, Related Work e a primeira metade de System Description, com bibliografia verificada e as primeiras figuras reais do manipulador. À tarde, decidimos pausar a escrita e voltar para o hardware: testar o braço RV-M2 direto do shiroi, sem depender da NUC, que segue inacessível (nem mDNS nem ZeroTier respondem, mesmo teste de sempre). Subi um roscore local e conectei os três Arduinos do braço via USB, sem intermediário nenhum entre o notebook e o robô.
O primeiro obstáculo foi trivial: o script de bring-up (preflight.py) morria no meio da execução com TypeError: 'bool' object is not callable. A causa era um sombreamento clássico de nome — uma variável local booleana e uma função do módulo tinham o mesmo identificador, test_limit_switches; ao tentar chamar a função, o Python resolvia para a variável. Corrigido em minutos. O resultado depois da correção, porém, era bem mais estranho do que o bug em si: nenhuma junta apresentava qualquer variação de pulse_count ao receber um comando de movimento — não só a Joint 1, que já carregava a suspeita de falha elétrica havia um mês, mas todas as seis.
Antes de suspeitar de firmware ou infraestrutura, a bancada mereceu uma checagem física completa. Multímetro nas três pontes H: 12V presentes nas três, alimentação de potência descartada como causa. Fins-de-curso da Joint 4 e da Joint 5 testados em ambas as posições, transições limpas. Cada suspeita física plausível — alimentação, sensor, driver — foi eliminada uma por uma, sem sobrar nenhuma explicação elétrica de pé.
A resposta real apareceu uma camada abaixo, nos logs da ponte rosserial: erros intermitentes de Input/output error na porta serial, e os três processos Python caindo ao mesmo tempo. A causa era o ModemManager, um serviço padrão do Linux que sonda todo dispositivo serial novo tentando identificar se é um modem celular — um conflito clássico e bem documentado entre Linux e Arduino, que corrompe o protocolo binário do rosserial no meio da comunicação. Parar o serviço destravou a comunicação, mas não resolveu o problema de verdade: mesmo com o link limpo, o campo de comando continuava sem efeito nenhum na telemetria de cada junta.
A causa raiz definitiva só apareceu comparando o repositório atual com um checkout antigo e esquecido do mesmo projeto, guardado numa pasta separada. Os três Arduinos estavam gravados com um firmware de bring-up — Joints12_test.ino e equivalentes — que nunca tinha sido trazido para este repositório. Esse firmware fica parado num menu de teste (test_mode=0) e ignora completamente os campos normais de setpoint que o controlador whole-body usa; ele reaproveita o campo GoHome da mensagem como um seletor de código de teste. Um mês inteiro de sessões concluindo “falha elétrica na Joint 1” na real testemunhava um firmware que nunca esteve escutando os comandos enviados — a comunicação parecia funcionar porque a telemetria de status continuava saindo normalmente, só o canal de comando é que caía no vazio.
Com o serviço parado e a tabela de códigos de teste em mãos, a Joint 1 e a Joint 4 giraram ao primeiro comando correto — confirmando motor, driver, alimentação e fins-de-curso saudáveis nas duas. O bloqueio do último mês nunca foi de hardware. Optamos por manter o firmware de teste em uso por enquanto (trazido para o repositório principal) em vez de regravar o operacional imediatamente. Um efeito colateral físico interessante apareceu durante os testes: com só uma junta sendo servo-controlada por vez, juntas vizinhas sem sustentação ativa cedem por gravidade ou se deslocam com o distúrbio do movimento — a Joint 2 e a Joint 3 precisaram de reposicionamento manual entre um teste e outro, usando a função de liberação de freio que o próprio firmware de teste expõe.
04 Ago 2026
O Bot de Review Quebrado e o Billing Separado do Claude Pro
O repositório do b166er tem um workflow de GitHub Actions que dispara uma revisão automática de código em toda PR — funcionou de forma consistente 14 vezes seguidas ao longo de julho, sempre em 30-45 segundos, e então quebrou do nada na PR que fechava o trabalho de ontem, com um erro genérico: Claude execution failed: result is_error:true. O log padrão do workflow não dizia mais que isso, e a primeira hipótese razoável — algo na configuração do plugin de revisão — não batia com a evidência: o log mostrava o plugin instalando com sucesso, a falha acontecia depois, durante a execução em si.
Em vez de trocar configuração às cegas, o caminho foi instrumentar antes de corrigir: liguei show_full_output temporariamente no workflow (numa PR própria, separada, para não misturar debug de CI com o trabalho de controle do robô) e rodei de novo. O log completo revelou a causa real — "error": "authentication_failed", HTTP 401, API key is invalid — depois de nove tentativas de autenticação ao longo de 3 minutos e meio antes de desistir. A chave de API configurada como secret do repositório, funcional havia um mês, simplesmente parou de autenticar.
Troquei a chave. O erro mudou, mas não sumiu: agora "error": "billing_error", HTTP 400, Credit balance is too low. Isso expôs uma confusão legítima — o Claude Pro (a assinatura usada para conversar comigo) e a API da Anthropic (o que esse workflow de CI efetivamente consome) são dois produtos com contas e billing completamente independentes. Ter uma assinatura Pro ativa não dá nenhum crédito de API; são dois medidores diferentes, cobrando por coisas diferentes. As 14 execuções anteriores provavelmente foram cobertas por um crédito de teste inicial da conta de API, esgotado silenciosamente sem nenhum aviso até a chave girar (ou ser revogada) e o próximo erro aparecer como autenticação, não como billing — dois sintomas em sequência escondendo a mesma causa raiz.
Com a conta de API carregada com crédito de verdade, uma execução chegou a custar US$1,39 em tokens — confirmação de que a chamada estava acontecendo de fato, não mais uma tentativa fantasma. Mas surgiu um terceiro comportamento, esse sem solução simples: o plugin de revisão dispara múltiplos sub-agentes em paralelo para escanear bugs, e na PR mais volumosa do lote (seis commits tocando vários pacotes de uma vez) esses sub-agentes ainda estavam rodando quando o tempo do job esgotou — sem erro, sem comentário publicado, um silêncio inconclusivo. PRs menores e de assunto único continuaram funcionando normalmente. A correção definitiva foi adicionar continue-on-error: true ao step do bot: uma ferramenta consultiva de revisão nunca deveria travar o pipeline de CI, seja qual for o motivo da falha — o repositório nem tem proteção de branch configurada, então esse erro nunca bloqueou um merge, mas o ruído de um check vermelho sem explicação clara custa atenção que não precisa ser gasta.
Fechei o dia documentando algo que nunca tinha sido escrito: o papel da branch local-state neste projeto. Ela não é uma feature branch de uso único — é reaproveitada sessão após sessão, sincronizada com main entre rodadas de trabalho em vez de recriada, já responsável por mais de 20 PRs ao longo do projeto. Uma convenção que sempre existiu na prática, mas só hoje ganhou uma linha no README.
03 Ago 2026
Bancada Bloqueada, Manobra Girar-Avançar-Girar em Simulação
A sessão começou tentando retomar de onde a bancada tinha parado — e esbarrou em dois bloqueios físicos antes de qualquer linha de código. A NUC não respondeu nem por mDNS (b166er-nuc.local) nem pela rota de fallback via ZeroTier: sem acesso, sem diagnóstico possível além do que já estava registrado. E o problema elétrico na Joint 1 (motor não gira mesmo com comunicação e firmware validados) segue exigindo multímetro na bancada, não isso aqui. Diante dos dois bloqueios, o caminho certo era migrar o trabalho para onde ainda havia terreno produtivo: a simulação, especificamente a lacuna que a Fase 3 tinha deixado propositalmente em aberto — o robô sem encoder de junta precisa de uma solução Fuzzy que lide com a base não-holonômica, e essa solução nunca tinha sido testada contra um cenário fiel ao hardware real.
Isso expôs um bug que estava dormindo desde a decisão arquitetural de 01 de julho. O state_estimator (que estima os ângulos de junta via IK a partir da T265 e da odometria do Pioneer, sem encoder) tinha um atalho: usar /joint_states real como semente do IK quando disponível, pensado como otimização inofensiva de Gazebo. O comentário no código dizia que, em hardware, esse tópico simplesmente “não existe” — só que isso deixou de ser verdade quando o modo hardware do launch unificado passou a subir o movemaster_control/state_publisher, o mesmo nó cujo pulse_count a bancada de ontem provou ser ruído sem encoder algum. O state_estimator estaria aceitando esse ruído como semente de alta confiança. A correção: o atalho vira opt-in (use_joint_states_seed, default false), e sem ele o sistema sempre usa sua própria estimativa recursiva do ciclo anterior — a mesma informação que o robô real vai ter disponível. Validado em Gazebo: convergência estável, sem regressão.
Com a estimativa honesta, sobrava encarar a limitação lateral em si: um alvo que exige movimento perpendicular ao heading do Pioneer nunca converge, porque a Jacobiana resolve a base como se ela pudesse se mover livremente em x e y (holonômica), e só depois da resolução uma projeção descarta a componente que a base skid-steer não consegue realizar de verdade. Entre as três correções possíveis — relaxar a orientação transitoriamente, empurrar o heading pelo espaço nulo, ou uma manobra discreta de girar-avançar-girar — a escolhida foi a manobra discreta, motivada por um requisito de projeto ainda mais à frente: tarefas futuras vão exigir o braço aplicar força contra resistência mecânica (romper uma trava), e só a manobra discreta cria um ponto de parada determinístico antes do contato — as outras duas corrigem de forma contínua, sem nenhum instante em que se possa afirmar “agora está alinhado o suficiente para empurrar”.
A implementação revelou dois bugs que só apareceram testando contra a física real do Gazebo, não em teoria. A primeira versão usava a direção crua da solução da Jacobiana como referência de giro — instável, porque o braço parado durante o giro continua preso rigidamente à base: girar a base faz o braço orbitar em torno do centro de giro, deslocando a “direção ótima” a cada ciclo de controle. O controlador perseguia um alvo que se movia por causa do próprio giro, sem nunca convergir. A correção trocou essa referência pelo rumo (bearing) até a posição XY fixa do alvo, geometricamente estável enquanto a base só gira no lugar. Mesmo assim, um segundo problema apareceu com alvos bem laterais: girar “no lugar” numa base skid-steer não é perfeito — o atrito de contato do motor de física do Gazebo desliza o suficiente para deslocar a posição real durante giros longos, e o rumo até o alvo muda mais rápido do que o giro consegue alcançar. A correção foi um teto de tempo: se o alinhamento não converge dentro de 8 segundos, desiste e libera o avanço em vez de girar para sempre — o mesmo efeito físico deve se repetir em hardware real, então o teto não é só uma gambiarra de simulação.
Enquanto validava a manobra visualmente, uma lacuna separada apareceu: nada publicava a pose real do Pioneer como TF, então o RViz sempre desenhava o robô plantado na origem, mesmo com a base andando de verdade no Gazebo — só o braço, via /joint_states, animava. A fonte do dado já existia (gazebo_sensor_sim já lia a pose ground-truth do /gazebo/model_states para montar /pioneer/pose); faltava só transmitir essa mesma pose como TF. Corrigido, e junto veio uma conversa sobre segurança que rendeu um item novo de ROADMAP: romper uma trava com o braço muda o perfil de risco do robô para tombamento, e existe um protótipo órfão no repositório (stability_controller.py, IMU → fator de estabilidade → parada de emergência) que nunca foi integrado ao stack atual — e mesmo integrado, não resolveria tudo, porque o robô não tem nenhum sensor de força: aplicar força contra uma trava hoje só pode ser feito às cegas.
07 Jul 2026
Primeira Tentativa de Calibração Hand-Eye na Bancada
O primeiro obstáculo do dia nem era do robô — era do ambiente. Subir o bringup da bancada (braço + T265 + detector de AprilTag) matava os três nós Arduino_1/2/3.py na largada, com ModuleNotFoundError: No module named 'imp'. A causa: o pacote conda ros-noetic-rosserial-python (canal robostack-noetic) ainda faz import imp, um módulo removido de vez do Python 3.12 — mesma classe de problema da correção do gazebo-ros de uma semana atrás, mas dessa vez sem build corrigido disponível no canal para eu simplesmente atualizar a versão. A solução foi um shim mínimo: o pacote só usa uma função do módulo (imp.find_module, só para checar se um pacote existe antes de importar), então um arquivo imp.py de 20 linhas reimplementando só essa função via importlib.util.find_spec, carregado na frente do PYTHONPATH, bastou.
Com o ambiente de pé, o objetivo do dia era começar a calibração hand-eye entre a T265 (montada no flange do braço) e o RV-M2 — o passo que destrava tanto a medição de erro real quanto o ajuste fino de PID e do Fuzzy em hardware. Escrevi um nó de coleta assumindo o desenho mais natural: perturbar o braço em pequenos passos em torno da pose atual, usar a TF Base→L5 do robot_state_publisher como a pose do flange, e o /b166er/tag_pose do detector de AprilTag como a pose da câmera — os dois lados do problema clássico AX=XB (Tsai-Lenz). Na bancada, esse desenho não sobreviveu ao primeiro contato com o hardware real.
O RV-M2 não tem mais encoder de junta — decisão de arquitetura já documentada aqui em 01 de julho, quando a estimativa de estado passou a vir da T265 e da odometria do Pioneer via IK, não de sensor de junta. O que eu não tinha medido ainda era a consequência prática dessa ausência para o firmware original de cada junta (src/arduino/Joints*.ino, fora do workspace catkin — não são pacotes ROS, só sketches). O controlador de cada placa calcula error = encoder_count - setpoint; sem encoder real alimentando encoder_count, esse erro nunca converge, o IsDone published nunca fica confiável, e a TF que o robot_state_publisher deriva do pulse_count publicado passou a refletir ruído de um pino flutuando, não a pose real do braço — inviabilizando ao mesmo tempo o assentamento por IsDone e a FK usada como verdade de calibração.
Ler o firmware por inteiro valeu a pena por outro motivo: toda placa dispara sozinha, na primeira conexão rosserial, uma rotina de homing (GoHome()) limitada por fim-de-curso físico — sem isso documentado em lugar nenhum, um fim-de-curso ausente teria significado o motor sendo empurrado indefinidamente atrás de um sensor inexistente. Confirmados os fins-de-curso presentes e funcionando, o homing automático é seguro. A leitura também expôs uma discrepância que ainda não tem resposta: as constantes HOME_X do firmware não são todas zero (HOME_2 = 106, HOME_3 = -123, em grau bruto de firmware), e não está validado se essa convenção bate com o q cinemático de kinematics.py usado pelo arm_vel_integrator — item que o ROADMAP já listava como não validado em hardware, e que segue em aberto.
A estratégia de coleta foi replanejada em campo: sem posição intermediária confiável, os únicos pontos com verdade física são o HOME (confirmado visualmente pelo operador contra as marcas de posicionamento do próprio manipulador, não só pela rotina automática do firmware) e cada fim-de-curso mecânico, saturando o setpoint muito além do curso da junta e assumindo o ângulo de catálogo já presente em JOINT_LOWER/JOINT_UPPER. A FK desses pontos de ancoragem passa a ser calculada direto de kinematics.py, não mais da TF. O dia fechou sem a coleta em si: religada a alimentação dos motores (uma bateria efetivamente descarregada explicou o primeiro round de “nada se move”), o firmware voltou a responder normalmente — mensagem chegando, malha de controle publicando status a ~19,5 Hz, erro calculado corretamente — mas a Joint 1 não girou fisicamente num comando real. Comunicação e lógica validadas de ponta a ponta isolam o problema na camada elétrica (driver, alimentação dos motores ou pino de enable), debug de bancada com multímetro que fica para a próxima sessão.
06 Jul 2026
Fase 3 Concluída e AprilTag Validado com a T265 Real
O braço estava tremendo. Depois do warm-start, o RV-M2 nunca parava de oscilar em torno do equilíbrio — um sintoma que parecia de ganho mal ajustado, mas a causa era mais fundamental: os PIDs de posição do ros_control não têm nenhuma noção de gravidade. Eles corrigem erro de posição, e só. Um braço horizontal ou inclinado tem torque gravitacional constante puxando cada junta para baixo, e um controlador que só reage ao erro depois que ele aparece está sempre um passo atrás da física. A correção foi um feedforward: gravity_torque_arm(q) calcula o torque gravitacional em cada junta a partir das massas do manual Mitsubishi, e o gazebo_arm_bridge soma esse torque ao comando de posição antes de mandar para o PID — o controlador deixa de brigar com a gravidade e passa a compensá-la preventivamente. Ajustar os ganhos por junta e resolver uma sequência load → unpause → switch que travava em deadlock quando o Gazebo estava pausado fechou essa frente.
O segundo sintoma era visual: no RViz, as quatro rodas do Pioneer apareciam colapsadas na origem, como um disco branco sobre o chassi. A causa raiz era boba e reveladora ao mesmo tempo — o código chamava rospy.WallRate, uma classe que não existe no ROS 1 (existe rospy.Rate e existe rospy.Duration, mas não essa combinação). O erro matava o nó pioneer_wheel_state_pub silenciosamente depois da primeira publicação, e o robot_state_publisher nunca mais recebia TF novo para as rodas. Reescrever o loop com time.sleep() em wall-time — imune a pausas do use_sim_time — resolveu de vez.
O terceiro problema foi o mais difícil de diagnosticar: o robô inteiro deslizava sozinho pelo Gazebo, sem nenhum comando de velocidade sendo enviado. O diagnóstico ao vivo, olhando /gazebo/link_states em tempo real, revelou um ciclo-limite de contato: a rigidez do contato das rodas estava subamortecida (kd=100 para um chassi de ~41 kg, resultando em ζ ≈ 1,6%), o robô entrava numa queda-e-recuperação perpétua contra o chão (vz ≈ -0,08 m/s de oscilação vertical), e o braço horizontal retificava essa vibração vertical em avanço horizontal contínuo — como um motor de vibração transformando ruído em deslocamento líquido. Subir o amortecimento (kd 100 → 1e4) quebrou o ciclo. Com isso resolvido, um piso de velocidade mínima da base (10 mm/s, contra micro-patinagem no contato ODE) e uma histerese no critério de parada do Fuzzy fecharam o ajuste fino. A validação final: amplitude de oscilação residual de 0,0002 rad/s, IK convergindo por mais de 60 segundos contínuos sem warning, e erro de rastreamento do end-effector de 4,7 mm contra um alvo estático — dentro do critério de 5 mm. A PR #20 foi mesclada, e a Fase 3 está oficialmente concluída, com uma limitação documentada e propositalmente deixada em aberto: alvos com erro puramente lateral estacionam a ~99 mm, porque a projeção não-holonômica da base não gera esterçamento para corrigir erro perpendicular ao heading — a estratégia de manobra fica para uma decisão futura de projeto.
Com a Fase 3 fechada, o ROADMAP das fases seguintes ganhou forma mais concreta: a Fase 4 absorveu o bringup completo da T265 (odometria + visão), a Fase 5 detalhou o Hokuyo UST-05LX para navegação, e ficou registrada a decisão de manter o Gazebo como simulador principal do projeto, com o PyBullet reservado como bancada auxiliar de sintonia rápida — não uma substituição, mas uma ferramenta complementar para iterar ganhos sem pagar o custo de subir o stack ROS completo a cada teste.
A peça que faltava para fechar a Fase 4 de visão era escolher a fonte do erro visual da servovisão. A T265 é a única RealSense do robô e não tem câmera de profundidade — só duas fisheye de ampla abertura (163° de FOV) e uma IMU. A resposta foi construir o pacote b166er_vision: um núcleo puro NumPy/OpenCV, testável sem ROS, com um nó fino por cima. A primeira armadilha apareceu na retificação: a função padrão do OpenCV para gerar uma câmera pinhole virtual a partir de uma fisheye (estimateNewCameraMatrixForUndistortRectify) degenera com o FOV da T265 — o fx calculado colapsava de 285 para 58, encolhendo os AprilTags na imagem até ficarem indetectáveis. A correção foi construir a pinhole virtual manualmente a partir do fx real da fisheye, sem deixar o OpenCV “otimizar” o resultado. Com a retificação estável, o detector de AprilTag 36h11 resolve a pose via solvePnPGeneric (IPPE_SQUARE), escolhendo entre as duas soluções possíveis a de menor erro de reprojeção e expondo a razão entre elas como pose_ambiguity — um sinal de alerta para quando a tag está quase de frente para a câmera, caso em que a posição continua confiável mas a orientação pode vir do ramo errado da ambiguidade. Seis testes sintéticos (renderizando uma tag em pose 3D conhecida e sintetizando a distorção fisheye inversa) validaram o pipeline inteiro sem precisar de ROS nem de hardware: erro de posição menor que 2% e de orientação menor que 3° em três poses distintas.
O teste que importava, porém, era com a câmera de verdade. Bancada montada no shiroi: T265 real, tag 36h11 de 132 mm impresso e exibido em tela, trena para medir a distância de referência. No ponto de calibração — tag centralizado no eixo óptico, a 33,8 cm — o pipeline estimou 33,3 cm: um erro de −1,5%, dentro da própria incerteza da trena, sem viés sistemático de escala. A 60 cm o erro subiu para +2,0%; a 147 cm, para +5,7%, com frames começando a se perder (a taxa caiu de 30 Hz para ~24 Hz). O padrão revelou uma regra prática que não estava óbvia antes do teste: o erro de escala é dominado pela resolução da tag em pixels, não pela distância em si — quanto menor a tag na imagem, maior o viés de fração de pixel nos cantos detectados (agravado pelo bloom da tela de exibição em uma câmera monocromática). A regra que ficou registrada no ROADMAP: lado da tag ≥ d/11, onde d é a distância de operação — um tag de 13 cm serve até ~1 m; para 1,5 m, é preciso imprimir 20 cm ou mais.
01 Jul 2026
IK Sincronizado, Braço Sem Encoders Documentado e Fase 2 Concluída
O dia começou com uma falsa esperança: o controlador whole-body subia, o Gazebo rodava, o end-effector se movia — mas a IK que estima os ângulos de junta a partir da câmera T265 não convergia. O resíduo de posição era de 0,24 metros. Não um erro numérico pequeno que tolerância mais generosa resolveria: 24 centímetros é uma falha de diagnóstico.
O culpado foi um problema de sincronização de timestamps que só existe em simulação. O nó gazebo_sensor_sim calcula a cinemática direta FK(q_t1) e publica a pose do T265 com o stamp t1 da mensagem de joint_states que usou. Enquanto isso, o state_estimator mantinha apenas o q mais recente na memória — que pode ter chegado em t2 > t1, com o braço já em outra posição. O IK tentava resolver a cinemática inversa de uma pose que correspondia a q_t1, mas usava como ponto de partida q_t2. Com o braço oscilando, a diferença |q_t1 - q_t2| era grande o suficiente para o algoritmo DLS divergir.
A correção foi cirúrgica: um buffer deque(maxlen=50) de pares (timestamp_em_segundos, q) no estimador. A cada ciclo, em vez de usar o q mais recente, o sistema procura no buffer o q cujo stamp é mais próximo do stamp do T265. Em simulação, onde o sensor_sim usa exatamente o stamp do joint_states, a diferença é zero — o IK recebe o seed exato e converge em zero iterações. O resíduo caiu de 0,24 m para 0,0015 m.
O segundo bug era mais visível: o RViz mostrava o Pioneer com apenas uma roda abaixo da plataforma, como se o modelo estivesse incompleto. O culpado era o pioneer_wheel_state_pub, que publicava mensagens antes do relógio de simulação estar ativo. Com use_sim_time=true, rospy.Time.now() retorna zero até a primeira mensagem chegar no tópico /clock. O robot_state_publisher recebia transformadas com stamp t=0 em loop, descartava tudo com o aviso TF_REPEATED_DATA, e as quatro rodas nunca apareciam. A correção: substituir o sleep de ROS por time.sleep() da stdlib Python — que usa o relógio real do sistema operacional independente do tempo de simulação — e aguardar até que rospy.Time.now() retorne um valor não-zero antes de começar a publicar.
Com a IK estável, a primeira tentativa de enviar um alvo revelou um terceiro problema: a pose z=0.35 m no frame odom colocava o end-effector quase 40 cm abaixo do ombro do braço, que fica a 0,74 m de altura. O braço precisaria cruzar o chassi do Pioneer para chegar lá — e tentava. O espaço de trabalho seguro começa em z ≥ 0,60 m. Para evitar que o braço caísse dentro do chassi nos 0,5 segundos que os controladores ros_control levam para carregar após o spawn, o launch passou a iniciar com J2=0.5 rad — ombro já elevado, esperando os controladores.
Com esses três ajustes, o pipeline end-to-end convergiu de forma limpa: o ee_target entra, o estimador de estado retorna q_est, o controlador Fuzzy calcula cmd_vel e arm_vel_cmd, o bridge integra velocidade em posição, e o Gazebo move o braço sem warnings. A PR #18 foi mesclada.
O restante do dia foi documentação. Escrevi um documento técnico completo explicando como o braço RV-M2 opera sem encoders de junta — o mecanismo de fechar a malha via T265 no espaço cartesiano, a IK com seed sincronizado por timestamp, a lei de controle whole-body com pseudoinversa amortecida e o diagrama de blocos completo do sistema, com o Fuzzy Mamdani como peça central. O README recebeu a tabela de pacotes, o launch unificado e a arquitetura resumida. O ROADMAP foi reescrito com seis fases claras, incluindo a redução do shaking do braço como próxima etapa — um problema esperado porque os PIDs de posição do ros_control não têm compensação de gravidade.
30 Jun 2026
Whole-Body Control: Pioneer e RV-M2 como um Único Sistema
A pergunta que organizou o trabalho de hoje foi simples mas consequente: o que significa controlar um manipulador móvel? A resposta óbvia — e errada — é sequencial: a base navega até perto do objeto, para, e então o braço age. Isso existe em abundância na literatura e em produtos comerciais. Não é o que estou construindo. O que me interessa é o caso em que a base e o braço se movem ao mesmo tempo, coordenados por um único planejador que trata os dois como partes de um mesmo sistema, sem distinção de quem navega e quem manipula. O objetivo da tarefa é a pose do end-effector no mundo — como chegar lá, usando as oito juntas disponíveis (três da base, cinco do braço), é problema do controlador, não meu.
O problema de controle que emerge dessa escolha tem um nome: whole-body control. O sistema tem oito graus de liberdade para satisfazer uma tarefa de seis (posição e orientação do end-effector), o que o torna redundante — sobram dois graus de liberdade que podem ser usados para otimizar objetivos secundários, como manter as juntas longe dos seus limites mecânicos. A lei de controle que implementei resolve isso com álgebra linear padrão: calcula a pseudoinversa amortecida da Jacobiana completa do sistema (6×8) para encontrar a velocidade das juntas que corrige o erro do end-effector, e projeta um objetivo secundário no espaço nulo para usar a redundância com propósito.
O ponto mais delicado foi estimar o estado do braço sem nenhum encoder — os motores do RV-M2 não retornam posição. A solução veio da combinação de dois sensores que já estão no robô: a T265 no end-effector diz onde o end-effector está no mundo; a odometria do Pioneer diz onde a base está. Com os dois, é possível calcular onde o braço precisa estar para que a cinemática seja consistente — o que é exatamente um problema de cinemática inversa. Ao custo de uma IK numérica a cada ciclo de controle (convergência típica abaixo de 0,1 mm), o sistema obtém uma estimativa dos ângulos de junta sem precisar de encoder algum.
O ganho do controlador foi substituído por um sistema de inferência Fuzzy do tipo Mamdani com três entradas (erro de posição, erro de orientação e variação do erro) e três saídas (ganho de posição, ganho de orientação e amortecimento da pseudoinversa). O efeito prático é que o robô se aproxima rápido quando está longe do alvo e desacelera suavemente ao chegar — e quando o erro começa a aumentar em vez de diminuir, o controlador eleva automaticamente o amortecimento para estabilizar o movimento antes de tentar de novo. Ao final do dia, o stack completo — estimador de estado, planejador whole-body e controlador Fuzzy — sobe com um único comando em dois modos: simulação com RViz e hardware real.
30 Jun 2026
Sensores no Lugar Certo e RViz Sem NUC
O URDF é o modelo que o ROS usa como verdade sobre a geometria do robô — é a partir dele que o sistema calcula onde cada sensor está, o que cada câmera vê e como as juntas do braço se relacionam com o chassi da base. Se os sensores estiverem nas posições erradas nesse arquivo, todo o raciocínio espacial do robô fica comprometido antes mesmo de começar. Hoje o trabalho foi exatamente esse: confrontar o modelo com uma foto do robô real e corrigir o que estava errado.
Três posições estavam imprecisas. A IMU Sparton AHRS-8 estava posicionada dentro do chassi do Pioneer — uma interpretação errada da descrição inicial. A foto mostrou que ela fica na face traseira do primeiro elo do braço, entre a cintura e o ombro, aparafusada diretamente no paralelepípedo azul que é o L1 do RV-M2. O Hokuyo estava flutuando alguns centímetros acima do top_plate, como se houvesse um espaçador inexistente; a correção o colocou assentado diretamente sobre a superfície preta do Pioneer. O braço em si estava deslocado em relação ao centro dos eixos das rodas — a plataforma foi estendida 10 cm na traseira para acomodar a bateria, e o braço fica na plataforma original, não na extensão. O processo foi iterativo: ajustar o arquivo Xacro, relançar o RViz, checar a posição visualmente, repetir.
Isso levantou um problema prático: o RViz só funcionava conectado à NUC, porque o script de ambiente apontava para ela como ROS master. Para desenvolver o URDF localmente, sem cabo e sem laboratório, foi preciso resolver dois obstáculos independentes. O primeiro era uma sessão antiga de Gazebo que deixou o parâmetro use_sim_time ativo no servidor de parâmetros do ROS — isso fazia o robot_state_publisher parar de publicar as transformações, esperando por um relógio de simulação que nunca chegava. A correção entrou direto no launch file do RViz. O segundo era mais fundo: a biblioteca de renderização 3D do RViz (OGRE) procura seus plugins num diretório do ambiente base do conda, mas no ambiente de ROS eles estão em outro lugar. A solução foi criar um link simbólico entre os dois caminhos — uma linha de terminal, mas que exigiu entender por que o OGRE ignora variáveis de ambiente e procura os plugins pelo caminho compilado dentro da biblioteca.
24 Jun 2026
Simulação Unificada e a Caça ao Bug do Gazebo
Até hoje, a simulação do b166er era um Frankenstein: um modelo combinado feito do zero com caixas e cilindros genéricos, sem nenhum sensor, desconectado das malhas 3D reais do Pioneer e do braço RV-M2 que já existiam espalhadas pelo repositório — resquício de tentativas anteriores que nunca foram unificadas. O trabalho de hoje foi exatamente isso: montar um único arquivo Xacro que reaproveita os modelos reais (com a geometria de cada peça do braço e os sonares do Pioneer) em vez de recriar tudo do zero, e plugar nele os três sensores que faltavam — a câmera Intel RealSense T265, o laser Hokuyo UST-05LX e a IMU Sparton AHRS-8.
A posição exata de cada sensor não foi chute: usei fotos do robô real para confirmar onde cada peça está montada de fato. A primeira tentativa colocou a T265 no chassi do Pioneer, pensando nela como sensor de navegação — errado. As fotos mostraram que ela fica no end-effector do braço, do lado do gripper, junto com um suporte que parece ter sido desenhado para até duas câmeras (só uma está em uso). A IMU, por sua vez, fica fixada na base do primeiro elo do braço, logo depois da torre giratória. O Hokuyo é o único que realmente fica em cima do Pioneer, na base superior. No caminho, também limpei seis sensores fictícios — quatro câmeras RGB simuladas e dois lasers de brinquedo — que vieram de um template antigo e nunca corresponderam a nada do robô real.
Com a versão cinemática validada no RViz, o passo natural era testar a física de verdade no Gazebo. Só que o simulador morria assim que abria, com um erro de baixo nível do glibc dentro de uma trava de mutex — nada relacionado ao robô. Isolei o problema removendo uma peça por vez até sobrar só o essencial: o gzserver puro funcionava, e o crash só acontecia quando o plugin que conecta o Gazebo ao ROS era carregado. Era um bug de empacotamento, não do modelo. A correção exigia uma versão mais nova desse plugin, que por sua vez exigia atualizar todo o ambiente Python de 3.11 para 3.12 — uma migração de centenas de pacotes, não um simples upgrade. Testei tudo num ambiente descartável antes de tocar no ambiente real, confirmei que o robô sobe e fica de pé no Gazebo com física rodando, e só então promovi a mudança — guardando uma cópia do ambiente antigo como rede de segurança, já que a validação final com o hardware físico só é possível no laboratório.
23 Jun 2026
T265 Validada e Acesso Remoto via ZeroTier
A T265 estava instalada desde a sessão anterior, mas instalada não é o mesmo que funcionando. Hoje o sensor passou pelo teste que importa: rodando em hardware real, publicando dados consistentes, com a pose mudando ao mover a câmera. O caminho até lá começou com um erro enganoso — RS2_USB_STATUS_ACCESS, que soa como permissão de sistema operacional, mas era outra coisa. A regra udev que dá acesso ao dispositivo USB tinha o VID (identificador de fabricante) errado: 8086 em vez do 8087 real da T265. Com o VID corrigido e o launch file convertido de um nó que não existia mais para o nodelet oficial do driver (rs_t265.launch), os números saíram dentro do esperado: odometria a 199.6 Hz, acelerômetro a 62.6 Hz, giroscópio a 199.5 Hz, e a transformação t265_odom_frame → t265_pose_frame atualizando em tempo real no RViz.
O segundo avanço do dia foi de infraestrutura, não de robótica: a conexão com a NUC do laboratório agora funciona por ZeroTier, uma rede privada virtual que conecta as máquinas como se estivessem na mesma LAN, independente de onde cada uma esteja fisicamente. Até aqui, o notebook só falava com a NUC quando os dois estavam na mesma rede local. Isso significa que o trabalho de integração não depende mais de estar no laboratório, ao lado do robô — útil em qualquer cenário onde o acesso físico é interrompido, da viagem à simples queda de energia. O script ros_env.sh foi atualizado para tentar ZeroTier primeiro e cair para mDNS (a descoberta automática usada quando notebook e NUC estão no mesmo Wi-Fi) como alternativa.
Por fim, o roadmap da integração foi formalizado em dois pilares que organizam o restante da tese: navegação autônoma da base Pioneer usando a T265 como fonte de odometria com move_base, e servovisão do braço Mitsubishi RV-M2 também apoiada na T265, sem depender dos encoders originais do braço.
22 Jun 2026
NUC Autônoma e Início da T265
Um robô que depende de intervenção manual para ligar não é um robô autônomo — é um eletrodoméstico glorificado. Hoje o b166er cruzou essa linha: a NUC agora inicializa completamente sozinha, sem teclado, sem senha, sem terminal aberto. O usuário liga a tomada e, em menos de dois minutos, o ROS já está no ar.
Para chegar lá, foi preciso resolver dois problemas independentes. O primeiro era o login: a NUC roda um ambiente gráfico (GDM), que por padrão exige senha na tela. Configurar o auto-login direto no GDM eliminou essa barreira — o usuário robo entra automaticamente na área de trabalho. O segundo era o roscore: o nó mestre do ROS precisa estar rodando antes que qualquer outro nó do sistema possa se comunicar. Em vez de depender de uma sessão de terminal aberta ou de um script iniciado manualmente, o roscore foi registrado como serviço do systemd — o gerenciador de processos do Linux — e agora sobe junto com o sistema operacional, logo após a rede e o mDNS estarem prontos.
O dia também marcou o início da integração da câmera Intel RealSense T265, responsável pela odometria visual do robô. A T265 usa duas câmeras fisheye e uma IMU interna para estimar a posição e orientação do robô no espaço sem depender de GPS ou de marcadores externos. As regras de dispositivo USB foram instaladas e o launch file do ROS foi criado — o teste com a câmera física fica para a próxima sessão.
21 Jun 2026
Integração via NUC
Minha tese investiga manipuladores móveis: robôs que combinam uma base capaz de se locomover livremente com um braço capaz de manipular objetos no espaço ao seu redor. Na maioria dos projetos de robótica, essas duas competências são tratadas como problemas separados — navegação de um lado, manipulação do outro. O ponto da pesquisa é justamente o que acontece quando elas precisam operar juntas, no mesmo robô, em tempo real.
O estágio atual é essa integração de fato. A plataforma móvel é uma base Pioneer 3-AT; o manipulador é um braço Mitsubishi RV-M2. Os dois nunca foram projetados para conversar entre si — cada um veio com seu próprio controlador, pensado para operar isolado. A ponte entre eles é um Intel NUC embarcado no robô, rodando ROS Noetic, responsável por sincronizar a navegação da base com o controle do braço numa arquitetura única, em vez de dois sistemas que nunca se falaram.