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:
359
scripts/feldbett_struktur.py
Normal file
359
scripts/feldbett_struktur.py
Normal 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 (A–A) [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.0–1.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 A–A
|
||||
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 A–A = {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!")
|
||||
Reference in New Issue
Block a user