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:
260
scripts/original/feldbett.py
Normal file
260
scripts/original/feldbett.py
Normal file
@@ -0,0 +1,260 @@
|
||||
"""
|
||||
Feldbett Konstruktion - FreeCAD Python Script
|
||||
=============================================
|
||||
Ausführen in FreeCAD: Menü → Makro → Makro ausführen → diese Datei wählen
|
||||
|
||||
Koordinatensystem:
|
||||
X = Längsrichtung (Liegeachse)
|
||||
Y = Höhe
|
||||
Z = Querrichtung (Breite)
|
||||
|
||||
Struktur:
|
||||
A = Längsstangen (2x, durchgehend, Z=±hwA, Y=totalH)
|
||||
Q = Querstreben (3x, quer in Z, kürzer als Breite, Y=legH)
|
||||
D = Diagonalen (12x, A-Knoten → Q-Ende, 3D-diagonal)
|
||||
B = Beine (12x, Q-Ende → Bodenknoten, gespiegelt zu D)
|
||||
F = Füße (kurze senkrechte Stücke unter Bodenknoten)
|
||||
"""
|
||||
|
||||
import FreeCAD as App
|
||||
import FreeCADGui as Gui
|
||||
import Part
|
||||
import math
|
||||
|
||||
# ============================================================
|
||||
# PARAMETER — hier anpassen
|
||||
# ============================================================
|
||||
|
||||
L = 35.0 # Stangenlänge [mm] — ALLE Stangen gleich lang
|
||||
BREITE_AA = 70.0 # Abstand zwischen den zwei Längsstangen (A–A) [mm]
|
||||
MODULE = 3 # Anzahl Module
|
||||
|
||||
# Rohr-Parameter
|
||||
ROHR_DA = 25.0 # Außendurchmesser Rohr [mm]
|
||||
ROHR_WAND = 2.0 # Wandstärke [mm]
|
||||
|
||||
FUSS_LAENGE = 10.0 # Länge der Fußstücke nach unten [mm]
|
||||
|
||||
# Farben (R, G, B) je 0.0–1.0
|
||||
FARBE_A = (0.20, 0.45, 0.75) # blau
|
||||
FARBE_D = (0.06, 0.43, 0.34) # grün
|
||||
FARBE_Q = (0.73, 0.46, 0.09) # amber
|
||||
FARBE_B = (0.42, 0.25, 0.63) # lila
|
||||
FARBE_F = (0.50, 0.50, 0.50) # grau
|
||||
|
||||
# ============================================================
|
||||
# BERECHNETE GEOMETRIE
|
||||
# ============================================================
|
||||
|
||||
hwA = BREITE_AA / 2.0 # halbe Breite A–A
|
||||
hwQ = L / 2.0 # halbe Q-Länge (Q = L lang)
|
||||
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"Mindest-L = {math.sqrt(dZ**2/0.75 + dZ**2/0.75*0.25):.1f} mm"
|
||||
)
|
||||
|
||||
legH = math.sqrt(legH2) # Höhe einer Ebene (A→Q oder Q→Boden)
|
||||
totalH = 2.0 * legH # Gesamthöhe
|
||||
step = 2.0 * L # Abstand Modul-zu-Modul (inkl. Lücke)
|
||||
aEnd = (MODULE - 1) * step + L # X-Ende der Längsstangen
|
||||
|
||||
# Kontrollrechnung D-Länge
|
||||
dLen = math.sqrt((L/2)**2 + legH**2 + dZ**2)
|
||||
|
||||
print("=" * 50)
|
||||
print("FELDBETT GEOMETRIE")
|
||||
print("=" * 50)
|
||||
print(f" Stangenlänge L = {L:.1f} mm")
|
||||
print(f" Breite A–A = {BREITE_AA:.1f} mm")
|
||||
print(f" Q-Länge = {L:.1f} mm (= L ✓)")
|
||||
print(f" Liegelänge (A-Ende) = {aEnd:.1f} mm")
|
||||
print(f" Höhe gesamt = {totalH:.1f} mm")
|
||||
print(f" legH (pro Ebene) = {legH:.2f} mm")
|
||||
print(f" D/B-Länge = {dLen:.2f} mm (sollte = {L:.1f})")
|
||||
print(f" Module = {MODULE}")
|
||||
print(f" Rohr DA/Wand = {ROHR_DA}/{ROHR_WAND} mm")
|
||||
print()
|
||||
|
||||
# ============================================================
|
||||
# HILFSFUNKTIONEN
|
||||
# ============================================================
|
||||
|
||||
ROHR_DI = ROHR_DA - 2.0 * ROHR_WAND # Innendurchmesser
|
||||
|
||||
def make_tube(p1, p2, label, color):
|
||||
"""
|
||||
Erzeugt ein Hohlrohr von Punkt p1 nach p2.
|
||||
p1, p2: FreeCAD.Vector
|
||||
"""
|
||||
direction = p2 - p1
|
||||
length = direction.Length
|
||||
|
||||
if length < 0.01:
|
||||
return None
|
||||
|
||||
# Außenzylinder
|
||||
outer = Part.makeCylinder(ROHR_DA / 2.0, length)
|
||||
# Innenzylinder (Hohlraum)
|
||||
inner = Part.makeCylinder(ROHR_DI / 2.0, length)
|
||||
tube = outer.cut(inner)
|
||||
|
||||
# Ausrichten: Zylinder liegt standardmäßig entlang Z-Achse
|
||||
# → Rotation auf Zielrichtung
|
||||
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:
|
||||
# 180° Rotation um beliebige Querachse
|
||||
rot = App.Rotation(App.Vector(1, 0, 0), 180)
|
||||
else:
|
||||
rot = App.Rotation()
|
||||
|
||||
placement = App.Placement(p1, rot)
|
||||
tube.Placement = placement
|
||||
|
||||
# FreeCAD Objekt anlegen
|
||||
obj = doc.addObject("Part::Feature", label)
|
||||
obj.Shape = tube
|
||||
|
||||
# Farbe setzen
|
||||
if hasattr(obj, "ViewObject") and obj.ViewObject:
|
||||
obj.ViewObject.ShapeColor = color
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
def vec(x, y, z):
|
||||
return App.Vector(x, y, z)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# DOKUMENT ANLEGEN
|
||||
# ============================================================
|
||||
|
||||
doc_name = "Feldbett"
|
||||
if doc_name in App.listDocuments():
|
||||
App.closeDocument(doc_name)
|
||||
|
||||
doc = App.newDocument(doc_name)
|
||||
|
||||
# ============================================================
|
||||
# LÄNGSSTANGEN A (2x durchgehend)
|
||||
# ============================================================
|
||||
|
||||
make_tube(
|
||||
vec(0, totalH, -hwA),
|
||||
vec(aEnd, totalH, -hwA),
|
||||
"A_links", FARBE_A
|
||||
)
|
||||
make_tube(
|
||||
vec(0, totalH, +hwA),
|
||||
vec(aEnd, totalH, +hwA),
|
||||
"A_rechts", FARBE_A
|
||||
)
|
||||
|
||||
# ============================================================
|
||||
# MODULE
|
||||
# ============================================================
|
||||
|
||||
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
|
||||
|
||||
suffix = f"_M{m+1}"
|
||||
|
||||
# --- Querstrebe Q ---
|
||||
make_tube(
|
||||
vec(xQ, legH, -hwQ),
|
||||
vec(xQ, legH, +hwQ),
|
||||
"Q" + suffix, FARBE_Q
|
||||
)
|
||||
|
||||
# --- Diagonalen D (4 pro Modul) ---
|
||||
# Von A-Knoten (oben, außen) nach Q-Ende (mitte, innen)
|
||||
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),
|
||||
f"D_xAL_{side}{suffix}", FARBE_D
|
||||
)
|
||||
# rechter A-Knoten → Q-Ende
|
||||
make_tube(
|
||||
vec(xAR, totalH, zA),
|
||||
vec(xQ, legH, zQe),
|
||||
f"D_xAR_{side}{suffix}", FARBE_D
|
||||
)
|
||||
|
||||
# --- Beine B (4 pro Modul, gespiegelt zu D) ---
|
||||
for side, zS in [("L", -1), ("R", +1)]:
|
||||
zA = zS * hwA
|
||||
zQe = zS * hwQ
|
||||
|
||||
# Q-Ende → linker Bodenknoten
|
||||
make_tube(
|
||||
vec(xQ, legH, zQe),
|
||||
vec(xAL, 0, zA),
|
||||
f"B_xAL_{side}{suffix}", FARBE_B
|
||||
)
|
||||
# Q-Ende → rechter Bodenknoten
|
||||
make_tube(
|
||||
vec(xQ, legH, zQe),
|
||||
vec(xAR, 0, zA),
|
||||
f"B_xAR_{side}{suffix}", FARBE_B
|
||||
)
|
||||
|
||||
# --- Füße F (4 pro Modul, senkrecht nach unten) ---
|
||||
for xF in [xAL, xAR]:
|
||||
for zS in [-1, +1]:
|
||||
zA = zS * hwA
|
||||
make_tube(
|
||||
vec(xF, 0, zA),
|
||||
vec(xF, -FUSS_LAENGE, zA),
|
||||
f"F_x{int(xF)}_z{int(zA)}{suffix}", FARBE_F
|
||||
)
|
||||
|
||||
# ============================================================
|
||||
# ABSCHLUSS
|
||||
# ============================================================
|
||||
|
||||
doc.recompute()
|
||||
|
||||
# Kamera auf Objekt ausrichten
|
||||
try:
|
||||
Gui.activeDocument().activeView().fitAll()
|
||||
Gui.SendMsgToActiveView("ViewFit")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
print("Feldbett erfolgreich erstellt!")
|
||||
print(f" Objekte im Dokument: {len(doc.Objects)}")
|
||||
print()
|
||||
print("STÜCKLISTE:")
|
||||
print(f" A Längsstangen : 2 Stück à {aEnd:.0f} mm")
|
||||
print(f" Q Querstreben : {MODULE} Stück à {L:.0f} mm")
|
||||
print(f" D Diagonalen : {MODULE*4} Stück à {dLen:.1f} mm")
|
||||
print(f" B Beine : {MODULE*4} Stück à {dLen:.1f} mm")
|
||||
print(f" F Füße : {MODULE*4} Stück à {FUSS_LAENGE:.0f} mm")
|
||||
print()
|
||||
print("CONNECTOR-WINKEL:")
|
||||
alpha_AD = math.degrees(math.acos(
|
||||
abs(App.Vector(1,0,0).dot(App.Vector(L/2,-legH,-dZ).normalize()))
|
||||
))
|
||||
print(f" Connector 1 — Winkel A↔D : {alpha_AD:.1f}°")
|
||||
print(f" Connector 2 — Winkel Q↔D : 60.0°")
|
||||
print(f" Connector 2 — Winkel D↔B : 90.0°")
|
||||
Reference in New Issue
Block a user