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,359 @@
"""
Feldbett Struktur v2 — FreeCAD Python Script
=============================================
Zeigt das komplette Feldbett mit allen Rohren in Realgröße.
A-Stangen sind segmentiert (je 350mm), Stift-Verbinder angedeutet.
Ausführen: FreeCAD → Makro → Makro ausführen → diese Datei wählen
Koordinatensystem:
X = Längsrichtung (Liegeachse)
Y = Höhe (oben = Liegefläche)
Z = Querrichtung (Breite)
Rohrtriplet "Komfortabel":
Standard : Alu 25×1.5 mm (Strukturrohre)
Stift : Alu 18×1.0 mm (Inline-Verbinder A-Stangen)
Hülse : Stahl 33.7×2.5 mm (Konnektoren C1/C2)
"""
import FreeCAD as App
import FreeCADGui as Gui
import Part
import math
# ============================================================
# PARAMETER
# ============================================================
# Geometrie
L = 350.0 # Stangenlänge [mm] — ALLE Stangen gleich
BREITE_AA = 700.0 # Abstand zwischen Längsstangen (AA) [mm]
MODULE = 3 # Anzahl Module
# Rohre — Triplet "Komfortabel"
# Standard (Alu 25×1.5)
STD_DA = 25.0 # Außendurchmesser [mm]
STD_WAND = 1.5 # Wandstärke [mm]
STD_DI = STD_DA - 2.0 * STD_WAND # 22.0 mm
# Stift-Verbinder (Alu 18×1.0)
STIFT_DA = 18.0
STIFT_WAND = 1.0
STIFT_DI = STIFT_DA - 2.0 * STIFT_WAND
STIFT_LAENGE = 100.0 # 50mm Einstecktiefe pro Seite
# Hülse / Konnektor (Stahl 33.7×2.5)
HUEL_DA = 33.7
HUEL_WAND = 2.5
HUEL_DI = HUEL_DA - 2.0 * HUEL_WAND # 28.7 mm
HUEL_LAENGE = 40.0 # Länge pro Hülsenstück
# Farben (R, G, B) je 0.01.0
FARBE_A = (0.20, 0.45, 0.75) # blau — Längsstangen
FARBE_D = (0.06, 0.43, 0.34) # grün — Diagonalen
FARBE_Q = (0.73, 0.46, 0.09) # amber — Querstreben
FARBE_B = (0.42, 0.25, 0.63) # lila — Beine
FARBE_STIFT = (0.85, 0.55, 0.15) # orange — Stift-Verbinder
FARBE_C1 = (0.85, 0.35, 0.15) # rot-orange — Connector 1
FARBE_C2 = (0.15, 0.55, 0.75) # blau-grün — Connector 2
# ============================================================
# BERECHNETE GEOMETRIE
# ============================================================
hwA = BREITE_AA / 2.0 # halbe Breite AA
hwQ = L / 2.0 # halbe Q-Länge (Q = L)
dZ = hwA - hwQ # Z-Einzug von A nach Q
legH2 = 0.75 * L**2 - dZ**2
if legH2 <= 0:
raise ValueError(
f"Geometrie unmöglich: L={L} zu kurz für Breite={BREITE_AA}. "
f"Erhöhe L oder reduziere BREITE_AA."
)
legH = math.sqrt(legH2) # Höhe pro Ebene (A→Q oder Q→Boden)
totalH = 2.0 * legH # Gesamthöhe
step = 2.0 * L # Modul-Abstand in X
aEnd = (MODULE - 1) * step + L # X-Ende der Längsstangen
# Kontrollrechnung D/B-Länge
dLen = math.sqrt((L / 2)**2 + legH**2 + dZ**2)
# A-Stangen Segmente
a_per_side = int(aEnd / L)
print("=" * 55)
print("FELDBETT STRUKTUR v2")
print("=" * 55)
print(f" Stangenlänge L = {L:.0f} mm")
print(f" Breite AA = {BREITE_AA:.0f} mm")
print(f" Liegelänge = {aEnd:.0f} mm = {aEnd/10:.0f} cm")
print(f" Höhe gesamt = {totalH:.0f} mm = {totalH/10:.1f} cm")
print(f" legH (pro Ebene) = {legH:.1f} mm")
print(f" D/B-Länge = {dLen:.1f} mm (soll = {L:.0f})")
print(f" Module = {MODULE}")
print(f" A-Segmente pro Seite = {a_per_side}")
print(f"\n Standard : {STD_DA}×{STD_WAND} mm (Alu)")
print(f" Stift : {STIFT_DA}×{STIFT_WAND} mm (Alu)")
print(f" Hülse : {HUEL_DA}×{HUEL_WAND} mm (Stahl)")
print(f" Spiel Hülse↔Std = {HUEL_DI - STD_DA:.1f} mm")
print(f" Spiel Std↔Stift = {STD_DI - STIFT_DA:.1f} mm")
print()
# ============================================================
# HILFSFUNKTIONEN
# ============================================================
def make_tube(p1, p2, da, wand, label, color, group=None):
"""Hohlrohr von p1 nach p2."""
direction = p2 - p1
length = direction.Length
if length < 0.01:
return None
di = da - 2.0 * wand
outer = Part.makeCylinder(da / 2.0, length)
inner = Part.makeCylinder(di / 2.0, length)
tube = outer.cut(inner)
z_axis = App.Vector(0, 0, 1)
norm_dir = direction.normalize()
cross = z_axis.cross(norm_dir)
dot = z_axis.dot(norm_dir)
if cross.Length > 1e-6:
angle = math.degrees(math.acos(max(-1.0, min(1.0, dot))))
rot = App.Rotation(cross, angle)
elif dot < 0:
rot = App.Rotation(App.Vector(1, 0, 0), 180)
else:
rot = App.Rotation()
tube.Placement = App.Placement(p1, rot)
obj = doc.addObject("Part::Feature", label)
obj.Shape = tube
if hasattr(obj, "ViewObject") and obj.ViewObject:
obj.ViewObject.ShapeColor = color
if group:
group.addObject(obj)
return obj
def vec(x, y, z):
return App.Vector(x, y, z)
def make_group(name):
return doc.addObject("App::DocumentObjectGroup", name)
# ============================================================
# DOKUMENT ANLEGEN
# ============================================================
doc_name = "Feldbett_v2"
if doc_name in App.listDocuments():
App.closeDocument(doc_name)
doc = App.newDocument(doc_name)
grp_A = make_group("A_Laengsstangen")
grp_Q = make_group("Q_Querstreben")
grp_D = make_group("D_Diagonalen")
grp_B = make_group("B_Beine")
grp_stift = make_group("Stift_Verbinder")
grp_c1 = make_group("Connector_1_Huelsen")
grp_c2 = make_group("Connector_2_Huelsen")
# ============================================================
# A — LÄNGSSTANGEN (segmentiert, 2× je a_per_side Stücke)
# ============================================================
for side, zS in [("L", -1), ("R", +1)]:
zA = zS * hwA
for seg in range(a_per_side):
x0 = seg * L
x1 = x0 + L
make_tube(
vec(x0, totalH, zA),
vec(x1, totalH, zA),
STD_DA, STD_WAND,
f"A_{side}_seg{seg+1}", FARBE_A, grp_A
)
# ============================================================
# STIFT-VERBINDER (A-Stangen inline, zwischen Segmenten)
# ============================================================
for side, zS in [("L", -1), ("R", +1)]:
zA = zS * hwA
for joint in range(a_per_side - 1):
xJ = (joint + 1) * L # Stoßstelle
x0 = xJ - STIFT_LAENGE / 2.0
x1 = xJ + STIFT_LAENGE / 2.0
make_tube(
vec(x0, totalH, zA),
vec(x1, totalH, zA),
STIFT_DA, STIFT_WAND,
f"Stift_{side}_{joint+1}", FARBE_STIFT, grp_stift
)
# ============================================================
# MODULE — Q, D, B + Konnektor-Hülsen
# ============================================================
for m in range(MODULE):
xStart = m * step
xAL = xStart # X linker A-Knoten
xAR = xStart + L # X rechter A-Knoten
xQ = xStart + L / 2 # X Mitte → Q-Position
suf = f"_M{m+1}"
# --- Q Querstrebe ---
make_tube(
vec(xQ, legH, -hwQ),
vec(xQ, legH, +hwQ),
STD_DA, STD_WAND,
"Q" + suf, FARBE_Q, grp_Q
)
# --- D Diagonalen (4 pro Modul) ---
for side, zS in [("L", -1), ("R", +1)]:
zA = zS * hwA
zQe = zS * hwQ
# linker A-Knoten → Q-Ende
make_tube(
vec(xAL, totalH, zA),
vec(xQ, legH, zQe),
STD_DA, STD_WAND,
f"D_AL_{side}{suf}", FARBE_D, grp_D
)
# rechter A-Knoten → Q-Ende
make_tube(
vec(xAR, totalH, zA),
vec(xQ, legH, zQe),
STD_DA, STD_WAND,
f"D_AR_{side}{suf}", FARBE_D, grp_D
)
# --- B Beine (4 pro Modul, gespiegelt) ---
for side, zS in [("L", -1), ("R", +1)]:
zA = zS * hwA
zQe = zS * hwQ
make_tube(
vec(xQ, legH, zQe),
vec(xAL, 0, zA),
STD_DA, STD_WAND,
f"B_AL_{side}{suf}", FARBE_B, grp_B
)
make_tube(
vec(xQ, legH, zQe),
vec(xAR, 0, zA),
STD_DA, STD_WAND,
f"B_AR_{side}{suf}", FARBE_B, grp_B
)
# --- Connector 2 Hülsen (an Q-Enden) ---
# Jedes Q-Ende bekommt 4 Hülsen: Q-links, Q-rechts, D(aufwärts), B(abwärts)
for zS in [-1, +1]:
zA = zS * hwA
zQe = zS * hwQ
qEnd = vec(xQ, legH, zQe)
side_label = "L" if zS < 0 else "R"
# Richtungsvektoren vom Q-Ende aus
# Q verläuft in ±Z → Hülsen zeigen in +Z und -Z
for qdir, qlabel in [((0,0,-1), "Ql"), ((0,0,+1), "Qr")]:
d = App.Vector(*qdir).normalize()
p1 = qEnd
p2 = vec(qEnd.x + d.x*HUEL_LAENGE, qEnd.y + d.y*HUEL_LAENGE, qEnd.z + d.z*HUEL_LAENGE)
make_tube(p1, p2, HUEL_DA, HUEL_WAND,
f"C2_{qlabel}_{side_label}{suf}", FARBE_C2, grp_c2)
# D-Richtung (vom Q-Ende zum A-Knoten oben = umgekehrte D-Richtung)
# Wir nehmen die Richtung zum linken A-Knoten als Referenz
vDr = vec(xAL - xQ, totalH - legH, zA - zQe).normalize()
p2 = vec(qEnd.x + vDr.x*HUEL_LAENGE, qEnd.y + vDr.y*HUEL_LAENGE, qEnd.z + vDr.z*HUEL_LAENGE)
make_tube(qEnd, p2, HUEL_DA, HUEL_WAND,
f"C2_D_{side_label}{suf}", FARBE_C2, grp_c2)
# B-Richtung (vom Q-Ende zum Bodenknoten)
vB = vec(xAL - xQ, 0 - legH, zA - zQe).normalize()
p2 = vec(qEnd.x + vB.x*HUEL_LAENGE, qEnd.y + vB.y*HUEL_LAENGE, qEnd.z + vB.z*HUEL_LAENGE)
make_tube(qEnd, p2, HUEL_DA, HUEL_WAND,
f"C2_B_{side_label}{suf}", FARBE_C2, grp_c2)
# --- Connector 1 Hülsen (an A-Knoten oben und Fußpunkten unten) ---
for xF in [xAL, xAR]:
for side, zS in [("L", -1), ("R", +1)]:
zA = zS * hwA
zQe = zS * hwQ
xi = int(xF)
# === C1 oben: am A-Knoten, verbindet A-Stange mit D-Diagonale ===
aNode = vec(xF, totalH, zA)
# Hülse entlang A (±X)
make_tube(
vec(xF - HUEL_LAENGE/2, totalH, zA),
vec(xF + HUEL_LAENGE/2, totalH, zA),
HUEL_DA, HUEL_WAND,
f"C1o_A_x{xi}_{side}{suf}", FARBE_C1, grp_c1
)
# Hülse entlang D (nach unten zum Q-Ende)
vD = vec(xQ - xF, legH - totalH, zQe - zA).normalize()
p2 = vec(aNode.x + vD.x*HUEL_LAENGE, aNode.y + vD.y*HUEL_LAENGE, aNode.z + vD.z*HUEL_LAENGE)
make_tube(aNode, p2, HUEL_DA, HUEL_WAND,
f"C1o_D_x{xi}_{side}{suf}", FARBE_C1, grp_c1)
# === C1 unten (Fuß): am Bodenknoten, verbindet B-Bein mit Bodenkontakt ===
fNode = vec(xF, 0, zA)
# Hülse für B (nach oben zum Q-Ende)
vBup = vec(xQ - xF, legH, zQe - zA).normalize()
p2 = vec(fNode.x + vBup.x*HUEL_LAENGE, fNode.y + vBup.y*HUEL_LAENGE, fNode.z + vBup.z*HUEL_LAENGE)
make_tube(fNode, p2, HUEL_DA, HUEL_WAND,
f"C1u_B_x{xi}_{side}{suf}", FARBE_C1, grp_c1)
# Hülse als Fuß (nach unten, senkrecht)
make_tube(
vec(xF, 0, zA),
vec(xF, -HUEL_LAENGE, zA),
HUEL_DA, HUEL_WAND,
f"C1u_F_x{xi}_{side}{suf}", FARBE_C1, grp_c1
)
# ============================================================
# ABSCHLUSS
# ============================================================
doc.recompute()
try:
Gui.activeDocument().activeView().fitAll()
Gui.SendMsgToActiveView("ViewFit")
except Exception:
pass
# Stückliste
print("STÜCKLISTE:")
print(f" {a_per_side * 2:>3}× A Längsstangen (Alu {STD_DA}×{STD_WAND} × {L:.0f}mm)")
print(f" {MODULE:>3}× Q Querstreben (Alu {STD_DA}×{STD_WAND} × {L:.0f}mm)")
print(f" {MODULE*4:>3}× D Diagonalen (Alu {STD_DA}×{STD_WAND} × {L:.0f}mm)")
print(f" {MODULE*4:>3}× B Beine (Alu {STD_DA}×{STD_WAND} × {L:.0f}mm)")
print(f" {'-'*50}")
print(f" {a_per_side*2 + MODULE + MODULE*8:>3}× Standardrohre gesamt")
print()
print(f" {(a_per_side-1)*2:>3}× Stift-Verbinder (Alu {STIFT_DA}×{STIFT_WAND} × {STIFT_LAENGE:.0f}mm)")
print(f" {24:>3}× Connector 1 (je 2 Hülsen Stahl {HUEL_DA}×{HUEL_WAND} × {HUEL_LAENGE:.0f}mm)")
print(f" { 6:>3}× Connector 2 (je 4 Hülsen Stahl {HUEL_DA}×{HUEL_WAND} × {HUEL_LAENGE:.0f}mm)")
print()
print(f" Objekte im Dokument: {len(doc.Objects)}")
print("\nFeldbett v2 erfolgreich erstellt!")