Conception et impression d’une bonde de piscine

Lors d’une folle après midi d’utilisation de la piscine, la bonde de fond de piscine a cédé.

Je pars pour imprimer sa remplaçante.

Trois solutions s’offrent à moi :

  • concevoir la pièce humainement (c’est comme avec l’arrivée des vélo electriques, on se sent obligé de trouver un adjectif pour definir l’ancien monde…)
  • trouver la pièce sur https://www.thingiverse.com/
  • concevoir la pièce avec l’aide de l’IA

Je n’ai pas trouvé la bonne pièce sur https://www.thingiverse.com/, donc je lance mon Claude et le MCP sur mon FreeCad.
Je lui envoie la photo ainsi que les deux dimensions clés : le diametre et l’entre axe.
Je lui indique aussi de chanfreiner les trous de vis et d’ajouter un renfort en étoile.

Mon explication pour les renforts…

La nouvelle bonde imprimée, PETG, cloisons 99%

bonde_v3_parametrique.py FreeCAD / Python
import FreeCAD as App, Part, math, os

# ================= PARAMETRES =================
D_OUT     = 174.0   # diametre total
ENTRAXE   = 158.0   # entraxe des 2 trous de vis
T         = 5.0     # epaisseur de la grille
D_SCREW   = 6.0     # percage des trous de vis
D_CSK     = 10.0    # diametre d'ouverture du fraisage (tete fraisee 9 mm)
D_CENTER  = 26.0    # diametre du trou central
R_HUB_OUT = 21.0    # rayon exterieur du moyeu plein
RIM_W     = 14.0    # largeur de la couronne exterieure (portee, SANS renfort)
N_RINGS   = 4       # anneaux de fentes
N_WEBS    = 6       # nervures radiales
WEB_W     = 6.0     # largeur des nervures radiales
WALL_R    = 5.0     # nervure circulaire entre deux anneaux
CH_EDGE   = 0.8     # chanfrein du bord exterieur
RIB_H     = 5.0     # hauteur des renforts en sous-face
RIB_W     = 6.0     # largeur des renforts radiaux
RIB_TAPER = 5.0     # longueur du raccord a 45 deg
RIB_MARGIN= 3.0     # retrait des renforts avant la couronne de portee
# ==============================================

R_OUT  = D_OUT/2.0
R_HOLE = ENTRAXE/2.0
R_GRID = R_OUT - RIM_W          # 73.0 : debut de la couronne de portee
R_RIB_END = R_GRID - RIB_MARGIN  # les renforts s'arretent ici
CSK_D  = (D_CSK - D_SCREW)/2.0  # profondeur du fraisage (cone a 90 deg)

name = "Bonde174v3"
if name in App.listDocuments(): App.closeDocument(name)
doc = App.newDocument(name)

pitch  = (R_GRID - R_HUB_OUT) / N_RINGS
slot_w = pitch - WALL_R

def sector(ri, ro, a1, a2):
    pie = Part.makeCylinder(ro, T+4, App.Vector(0,0,-2), App.Vector(0,0,1), a2-a1)
    pie.rotate(App.Vector(0,0,0), App.Vector(0,0,1), a1)
    return pie.cut(Part.makeCylinder(ri, T+8, App.Vector(0,0,-4)))

# ---------- grille ----------
cut = None
for i in range(N_RINGS):
    rm = R_HUB_OUT + i*pitch + WALL_R/2.0 + slot_w/2.0
    half = math.degrees((WEB_W/2.0 + slot_w/2.0) / rm)
    for k in range(N_WEBS):
        a0 = 360.0*k/N_WEBS
        a1, a2 = a0 + half, a0 + 360.0/N_WEBS - half
        s = sector(rm - slot_w/2.0, rm + slot_w/2.0, a1, a2)
        for aa in (a1, a2):
            t = math.radians(aa)
            s = s.fuse(Part.makeCylinder(slot_w/2.0, T+4,
                       App.Vector(rm*math.cos(t), rm*math.sin(t), -2)))
        cut = s if cut is None else cut.fuse(s)

shape = Part.makeCylinder(R_OUT, T).cut(cut.removeSplitter())

# ---------- renforts en sous-face (z = -RIB_H .. 0) ----------
ribs = Part.makeCylinder(R_HUB_OUT, RIB_H, App.Vector(0,0,-RIB_H))      # anneau central
ribs = ribs.cut(Part.makeCylinder(D_CENTER/2.0, RIB_H+4, App.Vector(0,0,-RIB_H-2)))
for k in range(N_WEBS):
    b = Part.makeBox(R_GRID+2, RIB_W, RIB_H, App.Vector(0, -RIB_W/2.0, -RIB_H))
    b.rotate(App.Vector(0,0,0), App.Vector(0,0,1), 360.0*k/N_WEBS)
    ribs = ribs.fuse(b)
ribs = ribs.cut(Part.makeCylinder(D_CENTER/2.0, RIB_H+4, App.Vector(0,0,-RIB_H-2)))
# raccord a 45 deg : on retire tout ce qui sort du cone -> hauteur nulle a R_GRID
outside = Part.makeCylinder(R_OUT+10, RIB_H, App.Vector(0,0,-RIB_H)).cut(
          Part.makeCone(R_RIB_END-RIB_TAPER, R_RIB_END, RIB_H,
                        App.Vector(0,0,-RIB_H), App.Vector(0,0,1)))
ribs = ribs.cut(outside)
shape = shape.fuse(ribs).removeSplitter()

# ---------- percages ----------
shape = shape.cut(Part.makeCylinder(D_CENTER/2.0, T+RIB_H+4, App.Vector(0,0,-RIB_H-2)))
for k in range(2):
    t = math.pi*k
    x, y = R_HOLE*math.cos(t), R_HOLE*math.sin(t)
    shape = shape.cut(Part.makeCylinder(D_SCREW/2.0, T+4, App.Vector(x,y,-2)))

# fraisage : chanfrein sur l'arete des percages, au niveau de la face superieure
ar = [e for e in shape.Edges
      if len(e.Vertexes) == 1
      and abs(e.BoundBox.ZMax - T) < 1e-6 and abs(e.BoundBox.ZMin - T) < 1e-6
      and abs(e.Length - 2*math.pi*(D_SCREW/2.0)) < 0.5]
print("aretes de percage sur la face superieure : %d (attendu 2)" % len(ar))
assert len(ar) == 2, "selection d'aretes incorrecte"
shape = shape.makeChamfer(CSK_D, ar)

def chamfer(sh, r, z, size):
    es = [e for e in sh.Edges if len(e.Vertexes)==1
          and abs(e.BoundBox.ZMax-z)<1e-6 and abs(e.Length-2*math.pi*r)<0.5]
    if not es: return sh
    try: return sh.makeChamfer(size, es)
    except Exception as ex:
        print("chanfrein ignore:", ex); return sh

shape = chamfer(shape, R_OUT, T, CH_EDGE)
shape = chamfer(shape, R_OUT, 0.0, CH_EDGE)
shape = chamfer(shape, D_CENTER/2.0, T, CH_EDGE)

obj = doc.addObject("Part::Feature", "Bonde"); obj.Shape = shape; doc.recompute()

# ---------- controles ----------
print("--- CONTROLE V3 ---")
bb = shape.BoundBox
sec = shape.section(Part.makePlane(400,400, App.Vector(-200,-200, T/2.0)))
print("diametre reel        : %.3f mm" % (2*max((v.Point.x**2+v.Point.y**2)**.5 for v in sec.Vertexes)))
print("hauteur totale       : %.3f mm  (Z de %.2f a %.2f)" % (bb.ZLength, bb.ZMin, bb.ZMax))
print("epaisseur grille     : %.3f mm | renfort : %.3f mm" % (T, -bb.ZMin))
cen = sorted({(round(f.BoundBox.Center.x,2), round(f.BoundBox.Center.y,2)) for f in shape.Faces
              if hasattr(f.Surface,"Radius") and abs(f.Surface.Radius-D_SCREW/2.0)<1e-6})
print("entraxe              : %.3f mm  %s" % (math.hypot(cen[-1][0]-cen[0][0], cen[-1][1]-cen[0][1]), cen))
csk = sorted({round(f.Surface.Radius,3) for f in shape.Faces
              if f.Surface.__class__.__name__=="Cone" and f.BoundBox.ZMax > T-0.01
              and abs(f.BoundBox.Center.x) > 10})
print("--- profil du fraisage (trou a x=+%.0f) ---" % R_HOLE)
for zc in [0.5, 1.5, 2.5, 3.0, 3.5, 4.0, 4.5, 4.95]:
    pl = Part.makePlane(400, 400, App.Vector(-200, -200, zc))
    sc = shape.section(pl)
    rmin = 1e9
    for e in sc.Edges:
        for pt in e.discretize(60):
            d = math.hypot(pt.x - R_HOLE, pt.y)
            if d < 12: rmin = min(rmin, d)
    if rmin < 1e8:
        print("  z=%4.2f mm  ->  diametre du percage = %6.3f mm" % (zc, 2*rmin))
print("attendu : %.1f mm jusqu'a z=%.1f puis elargissement jusqu'a %.1f mm en z=%.1f"
      % (D_SCREW, T-CSK_D, D_CSK, T))
# la couronne de portee doit etre strictement plane en sous-face
under = [f for f in shape.Faces if abs(f.BoundBox.ZMax) < 1e-6 and abs(f.BoundBox.ZMin) < 1e-6]
rmin_under = min((min((v.Point.x**2+v.Point.y**2)**.5 for v in f.Vertexes)) for f in under)
low = [f for f in shape.Faces if f.BoundBox.ZMin < -1e-6]
rmax_rib = max(max((v.Point.x**2+v.Point.y**2)**.5 for v in f.Vertexes) for f in low)
print("renfort : rayon max  = %.3f mm   (cible %.1f, couronne a %.1f)" % (rmax_rib, R_RIB_END, R_GRID))
print("retrait reel avant la couronne : %.3f mm" % (R_GRID - rmax_rib))
print("sous-face plane de r=%.3f a r=%.1f mm  -> disque exterieur intact : %s"
      % (rmax_rib, R_OUT, rmax_rib <= R_RIB_END + 1e-6))
print("solide valide        : %s | solides : %d | faces : %d" % (shape.isValid(), len(shape.Solids), len(shape.Faces)))
print("volume               : %.1f cm3" % (shape.Volume/1000.0))

out = r"C:\chemin\vers\ton\dossier"
doc.saveAs(os.path.join(out, "bonde_v3_174_entraxe158.FCStd"))
import MeshPart
m = MeshPart.meshFromShape(Shape=shape, LinearDeflection=0.04, AngularDeflection=0.15, Relative=False)
m.write(os.path.join(out, "bonde_v3_174_entraxe158.stl"))
print("STL : %d triangles" % m.CountFacets)
try:
    import FreeCADGui as Gui
    d = r"C:\chemin\vers\dossier"
    v = Gui.activeDocument().activeView()
    v.viewAxonometric(); Gui.SendMsgToActiveView("ViewFit"); v.saveImage(d+r"\v3_haut.png", 1200, 1000, "White")
    v.viewRear(); v.viewAxonometric()
    import FreeCAD
    v.setCameraOrientation(App.Rotation(App.Vector(1,0,0), 200).multiply(App.Rotation(App.Vector(0,0,1), 30)))
    Gui.SendMsgToActiveView("ViewFit"); v.saveImage(d+r"\v3_bas.png", 1200, 1000, "White")
except Exception as ex: print("vue:", ex)

La bonde réinstallée

Et ma contribution sur Thingiverse : https://www.thingiverse.com/thing:7410017

Autopsie d’une fugue canine

Notre chien Pitch a fugué pendant 4 jours autour du we du 12 décembre 2025.

Chez Lucca, nous sommes certifié ISO 27001, la certification de l’amélioration continue. Nous procédons à un Post Mortem pour chaque incident important. C’est l’occasion de tirer des enseignements et d’appliquer des corrections.

Déformation professionnelle oblige, je vais procéder au Post Mortem de la fugue de Pitch.

Post Mortem, Autopsie, c’est un peu mortel comme champ lexical, alors qu’on a retrouvé Pitch, il va bien.

Chronologie de l’incident

Vendredi 12 décembre

Retour de Paris le matin, changement de sac, départ pour le sport avec Nolwenn

Voici les traces de l’alarme de mes deux passages dans le vestiaire, on voit que la porte a bien été fermée dans les deux cas.

Pitch a du passer avec nous et se faufiler sous la porte du garage ensuite autour de 7:45.

Le garage est équipé d’une camera, mais l’historique est de 3h seulement.

La vie continue, on revient du sport, et je commence ma journée de travail.
C’est à 11:00 que l’on se rend compte que Pitch n’est plus à la maison. Quand on ramène notre fils Thomas de la gare, Pitch ne lui fait pas la fête, parce qu’il n’est pas là…

On fouille dans la maison, puis les alentours de la maison dans le quartier (nous habitons en ville, au Mans), rien.

J’ouvre un Gemini pour nous coacher.

Site web ou IA, l’idée est d’objectiver la recherche, la désémotionaliser, la rationaliser.

  1. Signaler le chien pucé perdu sur le site https://www.i-cad.fr/
    • S’il n’est pas pucé, appeler la fourrière animale pour signaler et décrire votre animal perdu
  2. Activer les réseaux : Objectif : augmenter le nombre de paires d’yeux qui cherche votre chien
    • Groupe WhatsApp de voisin
    • Groupes Facebook.
      • Pet Alert, demande de déclarer la perte sur un site tiers, on l’a fait : https://www.petalertglobal.com/. C’est du racket, mais ca augmente la visibilité de votre annonce, surtout avec l’approche de publicité ciblée sur Facebook. Votre annonce apparait sur des profils que vous ne connaissez pas et qui ne sont pas dans le groupe des chiens perdus.
      • filalapat, gratuit. C’est via le groupe facebook associé au site https://www.filalapat.fr/ que l’on a eu le plus d’information
  3. Accepter d’attendre, sans information.
  4. Rationnaliser avec des statistiques : ex : à quelle distance sont trouvés les chiens perdus

Message WhatsApp sur le groupe de voisin

Là on rage, je suis passé 10 minutes après, à 9:30 !

Il porte un collier avec son nom et notre numéro de téléphone :

Etape suivante, impression d’affiches et de flyers

Sur Facebook, les partages bâtent leur plein :

Avec des commentaires plus ou moins utiles :

Mais bon, on prend !

Heu, là, on ne prend pas…

C’est le groupe Filalapat qui a été le plus pertinent

Les réseaux d’amis jouent leur rôle :

Certains amis sont même partis à sa recherche !

Et encore, dimanche soir :

Et c’est reparti pour une exploration.
On explore à la torche pendant 2 heures cette zone :

Comme dans le jeu Scotland Yard, quand l’espion apparait, on a besoin de se déplacer pour aller sur les lieux, et lui a déjà bougé.

Un point qui est en notre faveur, c’est le fait que Pitch semble blessé d’après les témoignages, il est moins mobile.

Un nouveau SMS lundi matin, après 3 nuits loin de chez nous…

Message lundi, mais pour une « preuve de vie » datant de vendredi.

Le lendemain midi, j’explore ces zones là :

On a jamais autant abordé des inconnus, toujours bien accueillis.

Et là, Lundi 15h, Pitch n’a plus de jeton x2 (c’est une blague à propos du jeu Scotland Yard), on est appelé par un propriétaire de maison qui dit l’avoir dans son jardin.

Ni une ni deux, j’y vais.

C’est sur le point rouge, dans la même zone.
Depuis vendredi 15h, il n’a pas quitté la zone

Il est rentré dans un jardin par l’extérieur, par une zone piétonne

Il est là, c’est lui ! Délivrance ! Il est blessé à la patte arrière droite

Rendez vous en urgence chez le véto, il sera opéré le lendemain

Retour à la maison pour Pitch. Il a une fracture au talon non déplacée. Donc attelle pour 6 semaines. Il évite pour le moment la collerette de la honte mais n’a quand même pas trop fière allure …il a l’air d’aller bien, c’est le principal !

Dans un post-mortem, après la chronologie, c’est les causes racines.

Causes racines

  1. Pitch n’est pas castré. Je pense qu’il est sorti de la maison pour un « besoin primaire » (je ne sais pas si on peut encore dire ça comme ça…)

Circonstances aggravantes

  1. On a un collier GPS, mais
    • L’autonomie est faible (3h)
    • Pitch ne fuit jamais, enfin presque jamais, donc on ne le met plus.
  2. On lui avait mis un Air Tag, ce qui nous avait aidé à le retrouvé la première fois mais avec une attache non fiable, on passait plus de temps à chercher l’Air Tag que le chien. On a abandonné le système.
  3. La camera du garage n’a que 3h d’historique. Ce n’est pas critique, mais vendredi à midi, j’aurai bien aimé comprendre comment il a pu partir sans qu’on le voit passer la porte du garage !

Actions

  1. le collier avec le numéro de téléphone, c’est bien. mais ca ne localise pas. J’ai acheté un porte Air Tag résistant à tout, on a maintenant un Pitch 2.0

2. J’ai passé l’historique de l’enregistrement de la camera du garage à 30j.

3. Etude de la castration…

Recommandation si vous avez un chien ?

  1. le faire pucer.
  2. vérifier les coordonnées attachées à la puce, via le site https://www.i-cad.fr/.
  3. lui mettre un collier avec votre numéro de téléphone
  4. lui mettre un collier avec un Air Tag

Remerciements

Ces 4 jours ont été émotionnellement intenses !

Nous remercions particulièrement nos voisins et tous ces inconnus qui ont donné du temps pour nous permettre de retrouver notre chien Pitch !

Réalisation d’une table basse Chêne & Acier

Depuis plus de 5 ans, on parle de faire une table basse sans arriver à se mettre d’accord sur la forme, les matériaux, les usages. Et tout se débloque en trouvant deux inspirations que l’on va fusionner dans ce projet.

On est sur une table MASSIVE, acier et chêne.

  • 1m² pour le plateau carré, 5cm d’épaisseur, 38Kg !
  • Pieds en acier, plat de 70mm x 15mm, 6,2Kg par mètre, et j’en utilise quasi 6m : 46Kg de pieds.

Donc table basse de 84Kg, quand je disais MASSIVE, ce n’était pas que pour les matériaux !

Comme tous les projets « menuiserie », ça commence à la scierie. Je m’approvisionne à la scierie Girard.

Je procède au débit avec une grosse scie circulaire.

Petite simulation pour se rendre compte de la taille, et pour réfléchir à la hauteur.

Ensuite, c’est parti pour le process classique, dégauchisseuse, raboteuse.

Mise en situation :

Ensuite, rainurage pour assurer un bon maintien entre les différentes planches (en augmentant la surface de collage).

Encollage :

Puis serrage :

Au point de faire sortir la colle de partout.

Après une nuit de collage, sciage des deux cotés non rabotés, (les bouts).

Puis passage de la défonceuse. Un premier passage avec une fraise simple, sur un guidage d’une regle
et ensuite, un deuxième passage avec une fraise équipée d’un roulement.

Et enfin, un petit congé de 2mm de rayon avec une fraise faite pour ça, équipée aussi d’un roulement.

Ensuite, finition : 6 couches de vernis mat. J’aimerais bien qu’elle résiste à son utilisation.

Mise en situation avec des pieds temporaires, #agilité !

Passons aux pieds…

Après une petite galère pour trouver un fournisseur de métal ouvert aux particuliers, j’ai identifié l’entreprise Prolians au Mans, une mine !!! Des hangars à perte de vue remplis de tous les profils imaginables.

Par exemple, les tubes à section ronde.

Les tubes à section carrée.

Bref, j’ai acheté un plat en acier !

Souvent, nouveau projet rime avec nouvelles machines…

Me voici équipé d’une scie à ruban pour métal.

Et d’une nouvelle perceuse à colonne avec vitesse de rotation variable, c’est vraiment très bien. Si je l’avais eu pour mon projet de garde-corps, j’aurai cassé moins de forets, c’est sûr !

Sciage nickel, je rappelle que la section fait 7cm par 1,5cm !

Et en plus, ce we, j’avais des ouvriers !

Quel plaisir ! 17 ans nous séparent de cette photo… (Article du collage du parquet)

Thomas réalise des chanfreins à 45° pour faciliter la soudure

Et Erwan perce en 3 fois les trous permettant de fixer le plateau aux pieds

Place à la soudure

Erwan à la manœuvre :

Puis peinture

Enfin, vissage des pieds au plateau. Le serre-joint est mon ami dans ces moments où la force doit s’exprimer…

Anaëlle a fait tout un reportage, voici une photo « dans l’action » (on voit les vis qui s’insèrent nickel dans le montant)

Terminé !

Coût : ~240€

  • plat acier : 97€
  • chêne : ~130€
  • bombe de peinture noir satiné : 10€
  • 20 vis 5×50
  • 25 baguettes de soudure

Evidement, je ne cherche pas à amortir le coût des machines nécessaires pour ce chantier, en voici la liste :

  • Scies circulaires
  • Combiné rabot-dégo
  • Défonçeuse
  • Ponceuse
  • Scie à ruban métal
  • Perceuse à colonne
  • Poste à souder à l’arc
  • Meuleuse
  • Visseuse
  • Peugeot Expert Tepee 😉