Files
feldbett/scripts/feldbett_konnektoren.py
Axel Meyer d849c4e86d Initial commit: Feldbett Design-Dokumentation und FreeCAD-Scripts
Modulares Schwergewicht-Feldbett aus Alu-Rohren (25×1.5) mit Stahl-Konnektoren (33.7×2.5).
Design-Docs, Materialrecherche, Gewichtsberechnung, Korrosionsschutz-Analyse,
und zwei getestete FreeCAD-Makros (Struktur + Konnektoren-Detail).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:30:35 +02:00

404 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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: AD Gehrung = {saege_winkel(vA, vD):.1f}°")
print(f" C2: QD Gehrung = {saege_winkel(vQ, vDr):.1f}°")
print(f" C2: QB Gehrung = {saege_winkel(vQ, vB):.1f}°")
print(f" C2: DB 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 AD = {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 QD = {saege_winkel(vQ, vDr):.1f}°
Gehrungswinkel DB = {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).
""")