Guilhem Carmouze

Labo

Projets personnels

De petites études qui reprennent une question laissée ouverte par mon travail et y répondent par une mesure.

Ce sont des projets personnels, réalisés en octobre 2026 avec l’aide d’une IA : leurs commits portent la mention Co-Authored-By. Chaque nombre ci-dessous est reproduit par un script versionné dans le dépôt.

erpkit

Une boîte à outils NumPy testée pour la géométrie d’images 360° (équirectangulaire, cubemap, pinhole, rigs multi-vues), qui mesure ce que coûte vraiment l’assemblage de vues en panorama.

Dépôt : erpkit

Un résultat

5,6→1,06

Rapport de saut à la couture avec un écart d’exposition de ±5 % entre les vues, cube strict contre faces de 96° avec feathering (moyenne sur 3 graines aléatoires, 1 = invisible). Le feathering masque l’écart plutôt qu’il ne le corrige : le WS-PSNR reste à 35,5 dB.

Ce que montre la vidéo

Une caméra pinhole de 90° balaie un panorama équirectangulaire d’une pièce procédurale, son empreinte dessinée sur le panorama à côté de la vue extraite. En dessous, un rig de 14 vues est assemblé vue par vue sur une carte du nombre de vues qui voient chaque direction.

Origine

Le stage assemblait six vues pinhole en panoramas avec une cubemap à recouvrement. La taille des faces, le recouvrement et la largeur du fondu (feathering) sont des réglages dont ce projet quantifie le coût, avec son propre code et ses propres images.

Technologies

  • Python
  • NumPy
  • Pillow
  • pytest

microsplat

Du 3D Gaussian Splatting assez court pour se lire d’une traite : un rasteriseur de référence en NumPy, son jumeau différentiable en PyTorch, et des tests qui verrouillent chaque équation.

Dépôt : microsplat

Un résultat

33,6dB

PSNR sur des vues mises de côté de la scène de formes en lancer de rayons après 3 000 itérations à partir de gaussiennes aléatoires, avec contrôle adaptatif de la densité (moyenne de 3 graines aléatoires).

Ce que montre la vidéo

Quatre panneaux dans une optimisation qui part de gaussiennes aléatoires, puis une orbite de vues mises de côté : la cible en lancer de rayons, le rendu, l’erreur absolue et les contours des gaussiennes projetées.

Origine

Pendant le stage, tout passait par des moteurs de rendu existants : 3DGRUT, Splatfacto et DISCOVERSE. Ce projet ouvre le moteur de rendu : la passe avant réécrite d’après les articles, rendue différentiable, un test par équation.

Technologies

  • Python
  • NumPy
  • PyTorch
  • pytest

gaussian-projection-bench

Une étude numérique des deux façons de transformer une gaussienne 3D en gaussienne 2D, la linéarisation EWA et la transformée unscented, à travers des caméras pinhole, fisheye et équirectangulaires, contre une référence Monte-Carlo dont le plancher de bruit est lui-même rapporté.

Dépôt : gaussian-projection-bench

Un résultat

18→44px

Écart type du splat à partir duquel l’erreur de projection dépasse un demi-pixel (2-Wasserstein) à 45° hors axe dans une caméra pinhole : EWA avec le jacobien exact, puis la transformée unscented avec les points sigma de 3DGUT.

Ce que montre la vidéo

Une gaussienne 3D isotrope glisse vers le bord d’une image fisheye à 180°, puis vers le pôle d’une image équirectangulaire. Gris : la densité Monte-Carlo de sa projection. Orange : l’ellipse EWA. Bleu : l’ellipse de la transformée unscented et ses sept points sigma.

Origine

La plupart des moteurs de rendu gaussiens supposent une caméra en perspective, c’est pourquoi le stage rendait six vues pinhole et les assemblait. Ce projet mesure ce que coûte chaque approximation, en pixels, y compris près des pôles d’un panorama.

Technologies

  • Python
  • NumPy
  • Matplotlib
  • pytest

splat-navmap

Une étude de ce qui arrive quand une grille d’occupation découpée dans une scène de 3D Gaussian Splatting pousse un planificateur A* à traverser des murs ou à refuser une porte, sur des appartements synthétiques dont la vraie géométrie est connue.

Dépôt : splat-navmap

Un résultat

28,8→1,6%

Part des chemins qui entrent dans la géométrie réelle à un seuil d’opacité de 0,5, avec des défauts modérés : comptage des centres, puis accumulation d’empreintes (1 000 paires départ-arrivée sur 10 appartements synthétiques). La carte qui a le plus faible IoU planifie mieux (0,652 contre 0,663). Le comptage des centres est meilleur à 0,3, où 1,8 % des chemins entrent dans la géométrie réelle mais 4,9 % des paires deviennent inatteignables ; le plateau au-dessus de 0,5 découle des amplitudes de défauts que j’ai choisies.

Ce que montre la vidéo

Le seuil d’opacité balayé de 0,05 à 0,95 sur un appartement (graine 4) et une paire départ-arrivée choisie à la main. Pour le comptage des centres et pour l’accumulation d’empreintes : les gaussiennes vues de dessus, la grille extraite avec ses cellules fantômes et manquantes, et le chemin A*, avec des croix rouges là où il entre dans la géométrie réelle. Les gaussiennes sont des surfels synthétiques avec des défauts modélisés, pas des splats entraînés.

Origine

Mon rapport de stage dérivait une grille d’occupation d’une tranche d’une scène 3DGS fournie et planifiait dessus avec A*, en notant que le lien entre l’opacité des gaussiennes et la géométrie de collision n’était validé que partiellement. Ce projet l’étudie sur des appartements synthétiques à la géométrie connue. Rien du stage n’est réutilisé.

Technologies

  • Python
  • NumPy
  • SciPy
  • Matplotlib
  • pytest

cone-ekf-slam

Localisation EKF et EKF-SLAM sur des pistes de cônes de Formula Student simulées, vues à travers un champ de vision limité, avec les tests de cohérence (NEES, NIS) qui montrent quand l’incertitude annoncée par le filtre n’est plus fiable.

Dépôt : cone-ekf-slam

Un résultat

17 / 50→1 / 50

simulations où l’association au plus proche voisin a retenu un mauvais cône, avec une portée de capteur de 4 m (celle du simulateur 2D d’origine, 1,1 cône par scan) puis de 15 m (6,4 cônes par scan). 5 pistes, 10 graines de bruit chacune.

Ce que montre la vidéo

Une piste de cônes fermée vue de dessus : le chemin réel en gris, l’estimation d’EKF-SLAM en rouge, le secteur du capteur et les ellipses à 99 % de la pose et de chaque cône cartographié. L’incertitude de pose monte à 0,65 m (1 sigma) avant que les premiers cônes soient revus à t = 37,5 s, puis retombe à 0,03 m, et les ellipses des cônes hors de vue rétrécissent avec elle (graine de piste 7, 1,3 tour). Deux courbes en dessous suivent le NEES de la pose et les incertitudes.

Origine

Dans l’équipe driverless de TLSe Racing, ma part est le simulateur et le modèle de capteur à champ de vision qui choisit les cônes visibles par la voiture ; les planificateurs sont le travail de mes coéquipiers. Ce projet étudie l’étape entre les deux : estimer où sont la voiture et les cônes à partir de détections bruitées. Simulation uniquement : il n’a jamais tourné sur une voiture et ne partage ni code ni piste avec les dépôts de l’équipe.

Technologies

  • Python
  • NumPy
  • SciPy
  • Matplotlib
  • pytest

amr-traffic-lab

Une étude par simulation (graines aléatoires fixées) de l’intralogistique de l’usine connectée (Usine 4.0, Industrie 4.0) : combien de robots mobiles autonomes une allée d’usine peut accueillir avant de se bloquer, avec l’invariant « zéro collision » vérifié à chaque pas de temps.

Dépôt : amr-traffic-lab

Un résultat

0 / 600

simulations d’une heure bloquées avec le gestionnaire à réservation, sur trois plans. Dans l’atelier ouvert, il livre aussi 24 % de commandes de plus par heure que le gestionnaire naïf avec 16 robots (528,9 contre 427,2).

Ce que montre la vidéo

Le même plan d’usine rejoué deux fois, deux halls reliés par un couloir à voie unique, avec les mêmes 12 robots. À gauche : avec le gestionnaire naïf, les robots se rencontrent de face dans le couloir et s’arrêtent pour de bon. À droite : avec le gestionnaire à réservation, ils passent chacun à leur tour et le compteur de commandes livrées continue de monter.

Origine

Le planificateur du stage déplaçait un robot sur une carte vide. Ce projet ajoute le temps, une table de réservations et du Conflict-Based Search, et mesure la flotte. Il est indépendant du projet de promotion de dernière année sur l’Usine 4.0.

Technologies

  • Python
  • pytest
  • Matplotlib

usine40-cell-pipeline

Une cellule de production simulée d’usine connectée (Usine 4.0, Industrie 4.0) qui traverse OPC UA, MQTT, PostgreSQL et Grafana, avec l’OEE (taux de rendement synthétique) du tableau de bord vérifié contre le journal d’événements du simulateur, et chaque latence et chaque perte mesurées.

Dépôt : usine40-cell-pipeline

Un résultat

0,000pp

Plus grand écart entre l’OEE stocké par le pipeline et l’OEE recalculé à partir du journal d’événements du simulateur, sur 3 600 fenêtres de station de 30 s. Le seul moyen que j’ai trouvé de le casser est de perdre des échantillons : une coupure du broker de 60 s en QoS 0 laisse 45 fenêtres sur 240 avec un OEE faux.

Ce que montre la vidéo

Le tableau de bord Grafana en direct pendant un scénario scripté : production nominale, une panne injectée qui déclenche l’alerte de défaut, un arrêt de la passerelle qui fait passer la frise à NO DATA et déclenche l’alerte de données périmées, puis la reprise. Les images sont de vraies captures du tableau de bord provisionné ; seule la bande de légende est ajoutée.

Origine

À l’AIST, j’ai préparé une interface ROS 2 avec rejet des images périmées, limites de vitesse et minuterie de sécurité (dead-man). Ici, je voulais la même rigueur côté machines d’une usine : horodater chaque valeur à la source, ne jamais faire confiance à un message parce qu’il est arrivé, compter ce qui se perd. Il est indépendant du projet de promotion de dernière année sur l’Usine 4.0.

Technologies

  • Python
  • OPC UA
  • MQTT
  • PostgreSQL
  • Grafana
  • Docker
  • pytest

Images : MVTec AD (Bergmann et al., CVPR 2019), CC BY-NC-SA 4.0, adaptées (cartes d’anomalie et légendes ajoutées).

visual-quality-gate

Un contrôle qualité visuel sans entraînement pour une ligne d’usine connectée (Usine 4.0, Industrie 4.0) : PaDiM et PatchCore réimplémentés en PyTorch, mesurés sur cinq catégories de MVTec AD et jugés sur la question d’un responsable de ligne : combien de bonnes pièces je refuse pour arrêter combien de défauts ?

Dépôt : visual-quality-gate

Un résultat

10,5%

des bonnes pièces refusées par PatchCore WR50-10% quand son seuil, fixé sur des bonnes pièces mises de côté, vise 5 % : 43 sur 408 sur 3 graines, soit 2,1 fois la cible, alors que 6,3 % des pièces défectueuses (85 sur 1 353) passent quand même.

Ce que montre la vidéo

Cinq pièces de test notées par PatchCore WR50-10% (graine 0), chacune avec sa carte d’anomalie, son score, son seuil et son verdict OK ou NOK : le défaut attrapé médian, la bonne pièce acceptée médiane, le défaut attrapé le plus proche du seuil, la pire fuite (une pièce défectueuse acceptée) et le pire faux rejet (une bonne pièce refusée).

Origine

Pendant ma première année du cycle ingénieur, j’ai co-écrit avec un camarade de promotion un détecteur de balles de couleur en C pur, qui marche quand ce qu’on cherche est une couleur qu’on sait nommer d’avance. Ce projet en est le successeur à caractéristiques apprises : le détecteur ne voit que des bonnes pièces, et le seuil est lui aussi fixé sur des bonnes pièces. Il est indépendant du projet de promotion sur l’Usine 4.0.

Technologies

  • Python
  • PyTorch
  • NumPy
  • pytest