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>
This commit is contained in:
Axel Meyer
2026-04-09 14:30:35 +02:00
commit d849c4e86d
15 changed files with 2824 additions and 0 deletions

View File

@@ -0,0 +1,403 @@
"""
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).
""")