""" Feldbett Konnektoren Detail — FreeCAD Python Script ==================================================== Zeigt alle drei Verbindungstypen als Einzelteile zur Inspektion: 1. Connector 1 (C1) — A↔D / Fuß: 2 Hülsen auf Gehrung 2. Connector 2 (C2) — Q↔D↔B: 4 Hülsen sternförmig 3. Inline-Stift — A-Stangen Verlängerung Zusätzlich: Sägewinkel, Einstecktiefen, Passungsspiele. Ausführen: FreeCAD → Makro → Makro ausführen → diese Datei wählen Rohrtriplet "Komfortabel": Standard : Alu 25×1.5 mm Stift : Alu 18×1.0 mm Hülse : Stahl 33.7×2.5 mm """ import FreeCAD as App import FreeCADGui as Gui import Part import math # ============================================================ # PARAMETER # ============================================================ L = 350.0 BREITE_AA = 700.0 MODULE = 3 # Standard (Alu) STD_DA = 25.0 STD_WAND = 1.5 STD_DI = STD_DA - 2.0 * STD_WAND # Stift (Alu) STIFT_DA = 18.0 STIFT_WAND = 1.0 STIFT_DI = STIFT_DA - 2.0 * STIFT_WAND STIFT_LAENGE = 100.0 # Hülse (Stahl) HUEL_DA = 33.7 HUEL_WAND = 2.5 HUEL_DI = HUEL_DA - 2.0 * HUEL_WAND HUEL_LAENGE = 40.0 # Federbolzen FEDER_BOLZEN_D = 4.0 # Durchmesser [mm] # Farben FARBE_A = (0.20, 0.45, 0.75) FARBE_Q = (0.73, 0.46, 0.09) FARBE_D = (0.06, 0.43, 0.34) FARBE_B = (0.42, 0.25, 0.63) # Passspiel SPIEL_HUEL = HUEL_DI - STD_DA # 3.7 mm SPIEL_STIFT = STD_DI - STIFT_DA # 4.0 mm # ============================================================ # GEOMETRIE # ============================================================ hwA = BREITE_AA / 2.0 hwQ = L / 2.0 dZ = hwA - hwQ legH = math.sqrt(0.75 * L**2 - dZ**2) totalH = 2.0 * legH def norm(v): l = math.sqrt(sum(x**2 for x in v)) return tuple(x/l for x in v) # Richtungsvektoren (normiert) vA = (1.0, 0.0, 0.0) # A-Stange: +X vQ = (0.0, 0.0, 1.0) # Q-Strebe: +Z vD = norm(( L/2, -legH, -dZ)) # Diagonale: A→Q vDr = norm((-L/2, legH, dZ)) # umgekehrt: Q→A (aufwärts) vB = norm((-L/2, -legH, dZ)) # Bein: Q→Boden def angle_deg(v1, v2): dot = sum(a*b for a,b in zip(v1,v2)) return math.degrees(math.acos(max(-1.0, min(1.0, abs(dot))))) def angle_signed(v1, v2): dot = sum(a*b for a,b in zip(v1,v2)) return math.degrees(math.acos(max(-1.0, min(1.0, dot)))) def saege_winkel(v1, v2): """Gehrungswinkel: 90° - (Winkel_zwischen / 2)""" alpha = angle_signed(v1, v2) return 90.0 - alpha / 2.0 # ============================================================ # KONSOLEN-AUSGABE # ============================================================ SEP = "=" * 55 print(SEP) print("FELDBETT KONNEKTOREN — DETAIL") print(SEP) print(f"\nRohre:") print(f" Standard (Alu) : {STD_DA}×{STD_WAND} mm (ID={STD_DI:.1f})") print(f" Stift (Alu) : {STIFT_DA}×{STIFT_WAND} mm (ID={STIFT_DI:.1f})") print(f" Hülse (Stahl) : {HUEL_DA}×{HUEL_WAND} mm (ID={HUEL_DI:.1f})") print(f" Spiel Hülse↔Std : {SPIEL_HUEL:.1f} mm ({SPIEL_HUEL/2:.1f} mm pro Seite)") print(f" Spiel Std↔Stift : {SPIEL_STIFT:.1f} mm ({SPIEL_STIFT/2:.1f} mm pro Seite)") print(f"\nWinkel:") print(f" A ↔ D : {angle_deg(vA, vD):.1f}°") print(f" Q ↔ D : {angle_deg(vQ, vD):.1f}°") print(f" Q ↔ B : {angle_deg(vQ, vB):.1f}°") print(f" D ↔ B : {angle_deg(vD, vB):.1f}°") print(f"\nSägewinkel (Gehrung, Abweichung von 90°):") print(f" C1: A–D Gehrung = {saege_winkel(vA, vD):.1f}°") print(f" C2: Q–D Gehrung = {saege_winkel(vQ, vDr):.1f}°") print(f" C2: Q–B Gehrung = {saege_winkel(vQ, vB):.1f}°") print(f" C2: D–B Gehrung = {saege_winkel(vDr, vB):.1f}°") print() # ============================================================ # FreeCAD HILFSFUNKTIONEN # ============================================================ def fv(t): return App.Vector(t[0], t[1], t[2]) def rotation_to_dir(direction): z = App.Vector(0, 0, 1) d = App.Vector(*direction).normalize() cross = z.cross(d) dot = z.dot(d) if cross.Length > 1e-6: angle = math.degrees(math.acos(max(-1.0, min(1.0, dot)))) return App.Rotation(cross, angle) elif dot < 0: return App.Rotation(App.Vector(1, 0, 0), 180) else: return App.Rotation() def make_cyl(direction, length, radius, origin=(0,0,0)): cyl = Part.makeCylinder(radius, length) rot = rotation_to_dir(direction) cyl.Placement = App.Placement(App.Vector(*origin), rot) return cyl def make_huelse(direction, origin=(0,0,0), length=None): """Hohle Hülse (Stahl) entlang direction.""" ll = length or HUEL_LAENGE outer = make_cyl(direction, ll, HUEL_DA / 2.0, origin) inner = make_cyl(direction, ll + 1.0, HUEL_DI / 2.0, origin) return outer.cut(inner) def make_rohr(direction, length, da, wand, origin=(0,0,0)): """Allgemeines Hohlrohr.""" di = da - 2.0 * wand outer = make_cyl(direction, length, da / 2.0, origin) inner = make_cyl(direction, length + 1.0, di / 2.0, origin) return outer.cut(inner) def make_federbolzen_bohrung(direction, origin, dist_from_start): """Querbohrung für Federbolzen.""" if FEDER_BOLZEN_D <= 0: return None d = App.Vector(*direction).normalize() mid = App.Vector( origin[0] + d.x * dist_from_start, origin[1] + d.y * dist_from_start, origin[2] + d.z * dist_from_start, ) ref = App.Vector(0,1,0) if abs(d.dot(App.Vector(0,1,0))) < 0.9 else App.Vector(1,0,0) bohr_dir = d.cross(ref).normalize() bohr_len = HUEL_DA + 4.0 start = App.Vector( mid.x - bohr_dir.x * bohr_len / 2.0, mid.y - bohr_dir.y * bohr_len / 2.0, mid.z - bohr_dir.z * bohr_len / 2.0, ) cyl = Part.makeCylinder(FEDER_BOLZEN_D / 2.0, bohr_len) rot = rotation_to_dir((bohr_dir.x, bohr_dir.y, bohr_dir.z)) cyl.Placement = App.Placement(start, rot) return cyl def add_part(doc, shape, name, color, group=None): obj = doc.addObject("Part::Feature", name) obj.Shape = shape if obj.ViewObject: obj.ViewObject.ShapeColor = color if group: group.addObject(obj) return obj # ============================================================ # DOKUMENT # ============================================================ doc_name = "Feldbett_Konnektoren_v2" if doc_name in App.listDocuments(): App.closeDocument(doc_name) doc = App.newDocument(doc_name) grp_c1 = doc.addObject("App::DocumentObjectGroup", "Connector_1") grp_c2 = doc.addObject("App::DocumentObjectGroup", "Connector_2") grp_stift = doc.addObject("App::DocumentObjectGroup", "Inline_Stift") grp_demo = doc.addObject("App::DocumentObjectGroup", "Demo_Rohre") # Positionen der drei Baugruppen (nebeneinander in X) O_C1 = (0, 0, 0) O_C2 = (150, 0, 0) O_STIFT = (350, 0, 0) # ============================================================ # CONNECTOR 1 — 2 Hülsen (A + D) # ============================================================ print("Erzeuge Connector 1...") # Hülse A-Richtung (+X und -X, durchlaufend) h_A = make_huelse((1,0,0), O_C1) # Hülse A Gegenrichtung h_A2 = make_huelse((-1,0,0), O_C1) # Hülse D-Richtung h_D = make_huelse(vD, O_C1) c1 = h_A.fuse(h_A2).fuse(h_D) # Federbolzen-Bohrungen for direction in [(1,0,0), (-1,0,0), vD]: b = make_federbolzen_bohrung(direction, O_C1, HUEL_LAENGE * 0.6) if b: c1 = c1.cut(b) try: c1 = c1.makeFillet(1.0, c1.Edges) except Exception: pass add_part(doc, c1, "C1_komplett", (0.85, 0.35, 0.15), grp_c1) # Demo-Rohre in C1 einstecken (Alu-Stangen, halbtransparent) demo_A = make_rohr((1,0,0), 120, STD_DA, STD_WAND, (O_C1[0]-60, O_C1[1], O_C1[2])) obj_dA = add_part(doc, demo_A, "Demo_A_in_C1", (0.2, 0.45, 0.75), grp_demo) if obj_dA.ViewObject: obj_dA.ViewObject.Transparency = 60 demo_D = make_rohr(vD, 100, STD_DA, STD_WAND, O_C1) obj_dD = add_part(doc, demo_D, "Demo_D_in_C1", (0.06, 0.43, 0.34), grp_demo) if obj_dD.ViewObject: obj_dD.ViewObject.Transparency = 60 print(f" C1: 2+1 Hülsen à {HUEL_LAENGE:.0f}mm, Gehrung A-D = {saege_winkel(vA, vD):.1f}°") # ============================================================ # CONNECTOR 2 — 4 Hülsen (Ql, Qr, D, B) # ============================================================ print("Erzeuge Connector 2...") h_Ql = make_huelse((0,0,-1), O_C2) h_Qr = make_huelse((0,0,+1), O_C2) h_Dr = make_huelse(vDr, O_C2) h_Bv = make_huelse(vB, O_C2) c2 = h_Ql.fuse(h_Qr).fuse(h_Dr).fuse(h_Bv) # Federbolzen-Bohrungen for direction in [(0,0,-1), (0,0,+1), vDr, vB]: b = make_federbolzen_bohrung(direction, O_C2, HUEL_LAENGE * 0.6) if b: c2 = c2.cut(b) try: c2 = c2.makeFillet(1.0, c2.Edges) except Exception: pass add_part(doc, c2, "C2_komplett", (0.15, 0.55, 0.75), grp_c2) # Demo-Rohre in C2 for direction, length, label, color in [ ((0,0,-1), 80, "Demo_Ql_in_C2", FARBE_Q), ((0,0,+1), 80, "Demo_Qr_in_C2", FARBE_Q), (vDr, 80, "Demo_D_in_C2", (0.06, 0.43, 0.34)), (vB, 80, "Demo_B_in_C2", (0.42, 0.25, 0.63)), ]: r = make_rohr(direction, length, STD_DA, STD_WAND, O_C2) o = add_part(doc, r, label, color, grp_demo) if o.ViewObject: o.ViewObject.Transparency = 60 print(f" C2: 4 Hülsen à {HUEL_LAENGE:.0f}mm") print(f" Q-D Gehrung = {saege_winkel(vQ, vDr):.1f}°") print(f" D-B Gehrung = {saege_winkel(vDr, vB):.1f}°") # ============================================================ # INLINE-STIFT-VERBINDER (A-Stangen verlängern) # ============================================================ print("Erzeuge Inline-Stift...") # Zwei A-Rohrstücke + Stift dazwischen gap = 2.0 # kleiner Spalt zwischen den Rohren [mm] für Sichtbarkeit # linkes Rohr r_left = make_rohr((-1,0,0), 120, STD_DA, STD_WAND, (O_STIFT[0] - gap/2, O_STIFT[1], O_STIFT[2])) o = add_part(doc, r_left, "Stift_Rohr_links", FARBE_A, grp_stift) if o.ViewObject: o.ViewObject.Transparency = 40 # rechtes Rohr r_right = make_rohr((1,0,0), 120, STD_DA, STD_WAND, (O_STIFT[0] + gap/2, O_STIFT[1], O_STIFT[2])) o = add_part(doc, r_right, "Stift_Rohr_rechts", FARBE_A, grp_stift) if o.ViewObject: o.ViewObject.Transparency = 40 # Stift selbst (zentriert, ragt in beide Rohre) stift = make_rohr((1,0,0), STIFT_LAENGE, STIFT_DA, STIFT_WAND, (O_STIFT[0] - STIFT_LAENGE/2, O_STIFT[1], O_STIFT[2])) add_part(doc, stift, "Stift_Verbinder", (0.85, 0.55, 0.15), grp_stift) # Federbolzen-Löcher durch Rohr+Stift (beidseitig) for xOff in [-STIFT_LAENGE/4, +STIFT_LAENGE/4]: bohr = Part.makeCylinder(FEDER_BOLZEN_D / 2.0, STD_DA + 10) bohr.Placement = App.Placement( App.Vector(O_STIFT[0] + xOff, O_STIFT[1] - STD_DA/2 - 5, O_STIFT[2]), App.Rotation(App.Vector(1, 0, 0), 90) # senkrecht durch Rohr ) # Nur als Anschauung, nicht ausschneiden (Demo) print(f" Stift: {STIFT_DA}×{STIFT_WAND} × {STIFT_LAENGE:.0f}mm") print(f" Einstecktiefe: {STIFT_LAENGE/2:.0f}mm pro Seite") print(f" Federbolzen: Ø{FEDER_BOLZEN_D:.0f}mm, {STIFT_LAENGE/4:.0f}mm vom Stoß") # ============================================================ # CONNECTOR 2 EINZELTEILE (explodiert) # ============================================================ print("\nErzeuge C2 Einzelteile (explodiert)...") O_EXPL = (150, -120, 0) einzelteile = [ ((0, 0, -1), "C2_Teil_Ql", (0.7, 0.7, 0.2)), ((0, 0, +1), "C2_Teil_Qr", (0.7, 0.7, 0.2)), (vDr, "C2_Teil_D", (0.2, 0.7, 0.4)), (vB, "C2_Teil_B", (0.6, 0.3, 0.7)), ] for i, (direction, name, col) in enumerate(einzelteile): ox = O_EXPL[0] + (i - 1.5) * (HUEL_DA + 20) oy = O_EXPL[1] oz = O_EXPL[2] h = make_huelse(direction, (ox, oy, oz)) b = make_federbolzen_bohrung(direction, (ox, oy, oz), HUEL_LAENGE * 0.6) if b: h = h.cut(b) add_part(doc, h, name, col, grp_c2) # ============================================================ # ABSCHLUSS # ============================================================ doc.recompute() try: Gui.activeDocument().activeView().fitAll() except Exception: pass print() print(SEP) print("KONNEKTOREN ERFOLGREICH ERSTELLT") print(SEP) print(f""" Baugruppen im Dokument: Connector_1 — C1 komplett + Demo-Rohre Connector_2 — C2 komplett + Einzelteile (explodiert) + Demo-Rohre Inline_Stift — Stift in zwei A-Rohren Demo_Rohre — halbtransparente Alu-Rohre zur Veranschaulichung Connector 1 (×24): 2+1 Hülsen: A-links, A-rechts (durchlaufend), D Gehrungswinkel A–D = {saege_winkel(vA, vD):.1f}° Für Fuß: identisch, um 180° gedreht Connector 2 (×6): 4 Hülsen: Q-links, Q-rechts, D (aufwärts), B (abwärts) Gehrungswinkel Q–D = {saege_winkel(vQ, vDr):.1f}° Gehrungswinkel D–B = {saege_winkel(vDr, vB):.1f}° Inline-Stift (×8): Alu {STIFT_DA}×{STIFT_WAND} × {STIFT_LAENGE:.0f}mm Je 2 Federbolzen Ø{FEDER_BOLZEN_D:.0f}mm Tipp: Einzelne Gruppen ein-/ausblenden via Modellbaum (Leertaste). """)