Archives de catégorie : Uncategorized

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

Mise sur le réseau de l’imprimante 3D Discovery200 de Dagoma

Sur les conseils de @djojo13, j’ai étudié la mise en réseau de mon imprimante 3D.

J’ai donc découvert le monde de l’octoprint, une distribution de Linux pour Raspberry PI dédié au contrôle des imprimantes 3D. (si si, ça existe !)

J’ai suivi ce tutoriel et quelques dizaines de minutes après, mon imprimante était connectée à mon réseau wifi.

https://www.youtube.com/watch?v=MwsxO3ksxm4

Les fonctionnalités de bases sont :

– supervision des impressions : upload des fichiers .gcode, décompte, monitoring de la température, arrêt en cas de problème

– timelapse des impressions : si une webcam est branchée au Raspberry PI, OctoPrint capture une image à fréquence régulière et reconstitue une vidéo.

Vu le nombre de plugins, la communauté a l’air assez active.

Voici la liste des plugins que j’ai installés :

Capture_775

Capture_776

Histoire d’être notifié en fin d’impression

 

Cette configuration complexifie un peu l’installation.

IMG_20160207_102433

IMG_20160207_102440

Je vais devoir la déménager dans mon bureau, c’est sûr !

 

 

 

Conception et impression d’un support d’iPad pour un Peugeot Expert Tepee

Nouvellement équipé d’une imprimante 3D de chez Dagoma, je me suis essayé sur un nouvel objet, de la conception à la réalisation.

La V1 du support était faite à partir d’un cintre. Les avantages : coût et immédiateté… Par contre, l’esprit feng shui de la voiture en prenait un coup…

Nous voici donc sur la V2 :

La prise de cotes s’est faite dans la voiture et sur l’iPad.

Conception sur Sketchup

 



 

Impression :

 


Mise en place :

 

Publication du projet

Comme tout bon printeur, j’ai publié mes modèles sur le site communautaire thingiverse  : http://www.thingiverse.com/thing:1295882