Compare commits
10 Commits
6fac599ea0
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c9100e34e | |||
| 94906d9462 | |||
| a6898b1da7 | |||
| dca925727a | |||
| 97f1ad7b52 | |||
| a7f6f66537 | |||
| 32db0c6635 | |||
| 9a0a4d7320 | |||
| 8f2fc3842c | |||
| ab5427929f |
@@ -0,0 +1 @@
|
|||||||
|
.idea
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 179 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
@@ -1,3 +1,9 @@
|
|||||||
# Hoehenberechnungstool
|
# Hoehenberechnungstool
|
||||||
|
|
||||||
Berechnet die notwendige Höhe eines Trägers abhängig von der Anzahl der abhängenden AP-Strecken
|
Berechnet die notwendige Höhe eines Trägers abhängig von der Anzahl der abhängenden AP-Strecken
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
@@ -0,0 +1,508 @@
|
|||||||
|
from matplotlib import pyplot as plt
|
||||||
|
from matplotlib.widgets import Slider
|
||||||
|
from matplotlib.ticker import FormatStrFormatter
|
||||||
|
from math import floor, ceil
|
||||||
|
import ctypes
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileHeights:
|
||||||
|
|
||||||
|
# Ortsfaktor = Universalwert
|
||||||
|
_location_factor = 9.81 # m/s^2
|
||||||
|
|
||||||
|
# Sicherheitsfaktor gemäß Bauvorschrift
|
||||||
|
safety_factor = 1.7
|
||||||
|
|
||||||
|
# Fahrstreckengewichte nach Produktprogramm "OMNIFLO"
|
||||||
|
_rail_masses = {"AP110": 1.94, # K: Art-Nr. 821106001 - V: in kg
|
||||||
|
"APG110": 1.66, # K: Art-Nr. 821106002 - V: in kg
|
||||||
|
"APS110": 3.89, # K: Art-Nr. 821716001 - V: in kg
|
||||||
|
"AP60": 1.46 # K: Art-Nr. 821096001 - V: in kg
|
||||||
|
}
|
||||||
|
|
||||||
|
# Spulenzugsegment-Gewichte nach CAD
|
||||||
|
_train_segment_masses = {"T165": 0.38, # K: Art.-Nr. 826906201 - V: in kg
|
||||||
|
"T210": 0.248, # K: Art.-Nr. 826906213 - V: in kg
|
||||||
|
"T225": 0.355 # K: Art.-Nr. 826906200 - V: in kg
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
cross_section_type,
|
||||||
|
cross_section_width,
|
||||||
|
cross_section_thickness,
|
||||||
|
has_two_supports,
|
||||||
|
beam_length,
|
||||||
|
individual_rail_positions=None,
|
||||||
|
individual_rail_forces=None,
|
||||||
|
rail_type="AP110",
|
||||||
|
min_rail_distance=200,
|
||||||
|
min_pillar_distance=300,
|
||||||
|
material_yield_strength=235,
|
||||||
|
bobbin_train_segment_type="T165",
|
||||||
|
bobbin_mass=4,
|
||||||
|
colum_row_distance=3600,
|
||||||
|
pillar_width=100
|
||||||
|
):
|
||||||
|
|
||||||
|
self.min_rail_distance = min_rail_distance
|
||||||
|
self.min_dist_first_rail_to_pillar = min_pillar_distance
|
||||||
|
self.cross_section_type = cross_section_type
|
||||||
|
self.cross_section_width = cross_section_width
|
||||||
|
self.cross_section_thickness = cross_section_thickness
|
||||||
|
self.individual_rail_forces = individual_rail_forces
|
||||||
|
self.has_two_supports = has_two_supports
|
||||||
|
self.beam_length = beam_length
|
||||||
|
self.material_yield_strength = material_yield_strength
|
||||||
|
self.column_row_distance = colum_row_distance
|
||||||
|
self.rail_type = rail_type
|
||||||
|
self.individual_rail_positions = individual_rail_positions
|
||||||
|
self.pillar_width = pillar_width
|
||||||
|
self.rail_dist_from_support_a = self._set_rail_dist_from_support_a(individual_rail_positions)
|
||||||
|
self.train_segment_type = bobbin_train_segment_type
|
||||||
|
self.single_bobbin_mass = bobbin_mass
|
||||||
|
self.train_segment_mass = self._set_train_segment_mass()
|
||||||
|
self.rail_mass = self._set_rail_mass()
|
||||||
|
self.rail_forces = self._set_rail_forces()
|
||||||
|
self.bend_moments_from_a = self._summed_bend_moment_from_a()
|
||||||
|
self.support_bending_moment = self._set_support_a_bending_moment()
|
||||||
|
self.transverse_force_support_b = self._set_transverse_force_support_b()
|
||||||
|
self.transverse_force_support_a = self._set_transverse_force_support_a()
|
||||||
|
self._section_starts = self._set_beam_section_start_positions()
|
||||||
|
self.all_force_positions = self._set_all_force_positions()
|
||||||
|
self._beam_section_lengths = self._set_beam_section_lengths()
|
||||||
|
self._beam_section_forces = self._set_section_forces()
|
||||||
|
self.allowed_bend_stress = self._set_allowed_bend_stress()
|
||||||
|
self.bending_moments = self._set_bending_moments()
|
||||||
|
self.heights = self.all_profile_heights()
|
||||||
|
|
||||||
|
def _set_rail_dist_from_support_a(self, individual_rail_positions):
|
||||||
|
# Gibt Abstände zwischen den einzelnen AP-Profilen am Träger und der Säule zurück.
|
||||||
|
# Wenn 2 Säulen gegeben → Abstände zur "linken" Säule
|
||||||
|
|
||||||
|
# Fall 1: Es wurden keine individuellen Abstände zwischen den AP-Profilen und der Säule definiert.
|
||||||
|
# In diesem Fall berechnet die Methode die Abstände der AP-Schienen vom Lager "A" ausgehend unter
|
||||||
|
# Berücksichtigung der Mindestabstände zwischen den Schienen sowie zur Säule am Träger hängen kann.
|
||||||
|
|
||||||
|
if individual_rail_positions is None:
|
||||||
|
|
||||||
|
if self.has_two_supports:
|
||||||
|
distance_first_to_last_rail = (self.beam_length - self.pillar_width - 2 * self.min_dist_first_rail_to_pillar)
|
||||||
|
rail_amount = ceil(distance_first_to_last_rail / self.min_rail_distance)
|
||||||
|
distance_two_rails = distance_first_to_last_rail / (rail_amount - 1)
|
||||||
|
else:
|
||||||
|
distance_first_to_last_rail = self.beam_length - self.pillar_width / 2 - self.min_dist_first_rail_to_pillar
|
||||||
|
rail_amount = ceil(distance_first_to_last_rail / self.min_rail_distance)
|
||||||
|
distance_two_rails = distance_first_to_last_rail / (rail_amount - 1)
|
||||||
|
return [(self.min_dist_first_rail_to_pillar + self.pillar_width / 2) + (i * distance_two_rails) for i in range(rail_amount)]
|
||||||
|
|
||||||
|
# Fall 2: Es wurden individuelle Abstände zwischen den Lasten und der Säule definiert.
|
||||||
|
return self.individual_rail_positions
|
||||||
|
|
||||||
|
def _set_rail_forces(self):
|
||||||
|
# Gibt die Kräfte der einzelnen Fahrschienen am Träger zurück
|
||||||
|
|
||||||
|
# Fall 1: Es wurden keine individuellen Kräfte für die einzelnen Fahrschienen definiert.
|
||||||
|
# Methode berechnet dann für alle Fahrstrecken dieselbe Kraft.
|
||||||
|
if self.individual_rail_forces is None:
|
||||||
|
single_rail_force = ((self.train_segment_mass + self.rail_mass) * ProfileHeights._location_factor)
|
||||||
|
return [single_rail_force] * len(self.rail_dist_from_support_a)
|
||||||
|
|
||||||
|
# Fall 2: Es wurden individuelle Kräfte für die einzelnen Fahrstrecken definiert.
|
||||||
|
# Methode gibt die individuellen Kräfte unverändert zurück
|
||||||
|
return self.individual_rail_forces
|
||||||
|
|
||||||
|
def _set_train_segment_mass(self):
|
||||||
|
# Methode ermittelt das Spulenzuggewicht zwischen zwei Säulenreihen
|
||||||
|
|
||||||
|
bobbin_amount_per_meter = floor(1000 / int(self.train_segment_type.replace("T", ""))) # Anzahl Spulen pro Meter
|
||||||
|
|
||||||
|
bobbin_mass_per_meter = self.single_bobbin_mass * bobbin_amount_per_meter # Gesamtes Spulengewicht pro Meter
|
||||||
|
total_segment_mass_per_meter = 2 * ProfileHeights._train_segment_masses[self.train_segment_type] + bobbin_mass_per_meter
|
||||||
|
return total_segment_mass_per_meter * (self.column_row_distance / 1000) / 2 # kg pro Säulenreihe
|
||||||
|
|
||||||
|
def _set_rail_mass(self):
|
||||||
|
# Berechnet gesamtes Fahrstreckengewicht für Länge (Abstand) zwischen Säulenreihen zurück.
|
||||||
|
return ProfileHeights._rail_masses[self.rail_type] * self.column_row_distance / 1000
|
||||||
|
|
||||||
|
def _summed_bend_moment_from_a(self):
|
||||||
|
# Berechnet Gesamtmoment (Summe der Einzelmomente) am Balken vom Lager "A" ausgehend
|
||||||
|
return sum([force * distance for force, distance in zip(self.rail_forces, self.rail_dist_from_support_a)])
|
||||||
|
|
||||||
|
def _set_support_a_bending_moment(self):
|
||||||
|
# Berechnet Biegemoment im Lager "A", wenn Lager "A" ein dreiwertiges Lager ist (feste Einspannung)
|
||||||
|
if not self.has_two_supports:
|
||||||
|
return self.bend_moments_from_a
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _set_transverse_force_support_a(self):
|
||||||
|
# Berechnet Querkraft im Lager "A"
|
||||||
|
return abs(self.transverse_force_support_b - sum(self.rail_forces))
|
||||||
|
|
||||||
|
def _set_transverse_force_support_b(self):
|
||||||
|
# Berechnet Querkraft im Lager "B", wenn sowohl Lager "A", als auch Lager "B" existiert (nur bei Stützträgern)
|
||||||
|
if self.has_two_supports:
|
||||||
|
return self.bend_moments_from_a / self.beam_length
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _set_beam_section_start_positions(self):
|
||||||
|
# Bestimmt den Abstand der Bereichsanfänge zum Lager "A"
|
||||||
|
section_start_positions = self.rail_dist_from_support_a.copy()
|
||||||
|
section_start_positions.insert(0, 0)
|
||||||
|
return section_start_positions
|
||||||
|
|
||||||
|
def _set_section_forces(self):
|
||||||
|
# Fügt Querkraft im Lager "A" hinzu, da immer linkes Schnittufer betrachtet wird.
|
||||||
|
# Dies ist notwendig, da Querkraft "A" im Flächenschwerpunkt des Schnittes in allen Trägerbereichen ein
|
||||||
|
# Moment erzeugt
|
||||||
|
section_forces = self.rail_forces.copy()
|
||||||
|
section_forces.insert(0, self.transverse_force_support_a)
|
||||||
|
return section_forces
|
||||||
|
|
||||||
|
def _set_all_force_positions(self):
|
||||||
|
if self.has_two_supports:
|
||||||
|
all_force_positions = self._section_starts.copy()
|
||||||
|
all_force_positions.insert(len(all_force_positions), self.beam_length)
|
||||||
|
return all_force_positions
|
||||||
|
return self._section_starts
|
||||||
|
|
||||||
|
def all_forces(self):
|
||||||
|
all_forces = self._beam_section_forces.copy()
|
||||||
|
all_forces.insert(len(self.all_force_positions), self.transverse_force_support_b)
|
||||||
|
return all_forces
|
||||||
|
|
||||||
|
def _set_beam_section_lengths(self):
|
||||||
|
# Bereichslänge = Differenz zwischen nächster Kraftposition und vorherigen Kraftposition
|
||||||
|
return [new_pos - old_pos for new_pos, old_pos in zip(self.all_force_positions[1:], self.all_force_positions)]
|
||||||
|
|
||||||
|
def _set_bending_moments(self):
|
||||||
|
# Methode unterteilt Träger in Bereiche (vom linken Lager "A" ausgehend zum rechten Lager "b") und gibt die
|
||||||
|
# Schnittreaktionen (konkret Biegemomente) zurück. Aus diesen wird der Momentenverlauf gebildet.
|
||||||
|
|
||||||
|
section_bending_moments = []
|
||||||
|
subsection_lengths = self._beam_section_lengths.copy()
|
||||||
|
|
||||||
|
for i, (section_start, section_length) in enumerate(zip(self._section_starts, self._beam_section_lengths)):
|
||||||
|
|
||||||
|
section_moment = (self._beam_section_forces[0] * (section_start + section_length) - self.support_bending_moment)
|
||||||
|
summed_subsection_lengths = 0
|
||||||
|
|
||||||
|
for subsection_length, force in zip(subsection_lengths[:i], self._beam_section_forces[1:]):
|
||||||
|
summed_subsection_lengths += subsection_length
|
||||||
|
section_moment -= force * (section_start - summed_subsection_lengths + section_length)
|
||||||
|
|
||||||
|
section_bending_moments.append(abs(round((section_moment / 1000), 2)))
|
||||||
|
|
||||||
|
if self.has_two_supports:
|
||||||
|
section_bending_moments.insert(0, 0.0)
|
||||||
|
else:
|
||||||
|
section_bending_moments.insert(0, self.support_bending_moment / 1000)
|
||||||
|
|
||||||
|
return section_bending_moments
|
||||||
|
|
||||||
|
def reference_moment_of_resistance(self, bending_moment):
|
||||||
|
# Funktion berechnet Referenz-Widerstandsmoment für die aktuelle Trägerlänge
|
||||||
|
return bending_moment / self.allowed_bend_stress
|
||||||
|
|
||||||
|
def _set_allowed_bend_stress(self):
|
||||||
|
return (self.material_yield_strength * 1.2) / ProfileHeights.safety_factor
|
||||||
|
|
||||||
|
def recalculated_moment_of_resistance(self, height):
|
||||||
|
# Aktuelle Profilhöhe wird zur Ermittlung des derzeitigen Widerstandsmoments in die Widerstandsmoment-Formeln
|
||||||
|
# der unterschiedlichen Querschnittsarten eingesetzt.
|
||||||
|
|
||||||
|
inner_width = self.cross_section_width - 2 * self.cross_section_thickness
|
||||||
|
inner_height = height - 2 * self.cross_section_thickness
|
||||||
|
bar_width = self.cross_section_width - self.cross_section_thickness
|
||||||
|
|
||||||
|
# 1. Idealisiertes rechteckiges Hohlprofil:
|
||||||
|
if self.cross_section_type == "Hohlprofil":
|
||||||
|
return (self.cross_section_width * height ** 3 - inner_width * inner_height ** 3) / (6 * height)
|
||||||
|
|
||||||
|
# 2. Idealisiertes IPE-Profil / C-Profil:
|
||||||
|
elif self.cross_section_type in ["C-Profil", "IPE"]:
|
||||||
|
return (self.cross_section_width * height ** 3 - bar_width * inner_height ** 3) / (6 * height)
|
||||||
|
|
||||||
|
def all_profile_heights(self):
|
||||||
|
heights = []
|
||||||
|
|
||||||
|
bending_moments_converted = [i * 1000 for i in self.bending_moments] # Umrechnung von Nm in Nmm
|
||||||
|
|
||||||
|
for bending_moment, force_position in zip(bending_moments_converted, self.all_force_positions):
|
||||||
|
reference_moment_of_resistance = self.reference_moment_of_resistance(abs(bending_moment))
|
||||||
|
recalculated_moment_of_resistance = 0
|
||||||
|
height = 2 * self.cross_section_thickness
|
||||||
|
|
||||||
|
while recalculated_moment_of_resistance < reference_moment_of_resistance:
|
||||||
|
recalculated_moment_of_resistance = self.recalculated_moment_of_resistance(height)
|
||||||
|
height += 0.01
|
||||||
|
heights.append(height)
|
||||||
|
return heights
|
||||||
|
|
||||||
|
def current_value(self, current_beam_length, elements):
|
||||||
|
for i, force_position in enumerate(self.all_force_positions):
|
||||||
|
if current_beam_length <= force_position:
|
||||||
|
# m = Steigung, x = aktuelle Länge im derzeitigen Bereich, t = Höhe bei Bereichsbeginn
|
||||||
|
m = (elements[i] - elements[i - 1]) / self._beam_section_lengths[i - 1]
|
||||||
|
x = current_beam_length - self.all_force_positions[i - 1]
|
||||||
|
t = elements[i - 1]
|
||||||
|
return m * x + t
|
||||||
|
|
||||||
|
|
||||||
|
height_1 = ProfileHeights(cross_section_type="Hohlprofil",
|
||||||
|
cross_section_width=50,
|
||||||
|
cross_section_thickness=3,
|
||||||
|
individual_rail_positions=[200, 500, 700, 1100],
|
||||||
|
individual_rail_forces=[500, 500, 500, 500],
|
||||||
|
beam_length=1200,
|
||||||
|
min_rail_distance=200,
|
||||||
|
has_two_supports=True)
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================================================================================
|
||||||
|
# = PLOTTEN =
|
||||||
|
# ===========================================================================================================================
|
||||||
|
|
||||||
|
# Monitordaten für Figure-Größe
|
||||||
|
user32 = ctypes.windll.user32
|
||||||
|
user32.SetProcessDPIAware()
|
||||||
|
monitor_width = user32.GetSystemMetrics(0)
|
||||||
|
monitor_height = user32.GetSystemMetrics(1)
|
||||||
|
dpi = 120
|
||||||
|
|
||||||
|
fig, (ax_drawing, ax_bending_moment, ax_heights) = plt.subplots(nrows=3, ncols=1, sharex=True, figsize=(monitor_width / dpi, monitor_height / dpi))
|
||||||
|
|
||||||
|
# Globale Einstellungen
|
||||||
|
ax_drawing.set_title("Tool zur Bestimmung der Mindestquerschnittshöhe eines Profilträgers", fontsize=20)
|
||||||
|
subplot_adjust_left = 0.1
|
||||||
|
plt.subplots_adjust(left=subplot_adjust_left, right=1 - subplot_adjust_left, top=1 - subplot_adjust_left, bottom=subplot_adjust_left, hspace=0)
|
||||||
|
|
||||||
|
# Abszisseneinstellungen
|
||||||
|
x_min_val = -(max(height_1.all_force_positions) * 0.1)
|
||||||
|
x_max_val = max(height_1.all_force_positions) * 1.1
|
||||||
|
|
||||||
|
ax_heights.set_xlim(x_min_val, x_max_val)
|
||||||
|
ax_heights.set_xticks(height_1.all_force_positions)
|
||||||
|
ax_heights.xaxis.set_major_formatter(FormatStrFormatter("%.1f"))
|
||||||
|
|
||||||
|
# Einstellungen Biegemoment-Plot
|
||||||
|
ax_bending_moment.set_ylabel("Biegemoment [Nm]")
|
||||||
|
ax_bending_moment_y_max = max(height_1.bending_moments) * 1.25
|
||||||
|
ax_bending_moment.set_ylim(0, ax_bending_moment_y_max)
|
||||||
|
|
||||||
|
# Einstellungen Profilhöhen-Plot
|
||||||
|
ax_heights.set_xlabel("Kraftpositionen [mm]")
|
||||||
|
ax_heights.set_ylabel("Benötigte Profilhöhe [mm]")
|
||||||
|
|
||||||
|
ax_heights_y_max = max(height_1.heights) * 1.25
|
||||||
|
ax_heights.set_ylim(0, ax_heights_y_max)
|
||||||
|
ax_heights.set_xticks(height_1.all_force_positions)
|
||||||
|
|
||||||
|
# Berechnet horizontale Pixelanzahl der Plots
|
||||||
|
x_plot_px_count = monitor_width * (1 - 2 * subplot_adjust_left)
|
||||||
|
|
||||||
|
# Rechnet Längen der x-Achse in Pixel um (wie viele Pixel sind 1 mm?).
|
||||||
|
# Wichtig, damit die Pfeillängen der Kräfte unabhängig von der Länge des Balkens immer gleich lang dargestellt werden.
|
||||||
|
mm_in_px_converter = (abs(x_min_val) + abs(x_max_val)) / x_plot_px_count
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================================================================================
|
||||||
|
# = Zeichnung =
|
||||||
|
# ===========================================================================================================================
|
||||||
|
|
||||||
|
# Extremwerte der Zeichnungsordinate:
|
||||||
|
y_max_drawing = 300
|
||||||
|
ax_drawing.set_ylim(0, y_max_drawing)
|
||||||
|
ax_drawing.set_yticks([])
|
||||||
|
|
||||||
|
# Darstellung eines schematischen Trägers:
|
||||||
|
beam_y_pos_in_plot = y_max_drawing * 0.35
|
||||||
|
ax_drawing.hlines(beam_y_pos_in_plot, height_1.all_force_positions[0], height_1.all_force_positions[len(height_1.all_force_positions) - 1],
|
||||||
|
color="black", linewidth=3)
|
||||||
|
|
||||||
|
force_arrow_length = 0.5 * beam_y_pos_in_plot
|
||||||
|
|
||||||
|
|
||||||
|
def draw_transverse_force(x_pos, y_beam_pos, arrow_length, support_name):
|
||||||
|
|
||||||
|
# Darstellung des Balkenendes als Punkt mit Lagerbenennung
|
||||||
|
ax_drawing.plot(x_pos, y_beam_pos, "o", color="black")
|
||||||
|
|
||||||
|
# Pfeildarstellung für Querkraft
|
||||||
|
ax_drawing.annotate('', xytext=(x_pos, y_beam_pos), xycoords='data', xy=(x_pos, y_beam_pos - arrow_length), textcoords='data',
|
||||||
|
arrowprops=dict(color="red", width=1, headwidth=6))
|
||||||
|
|
||||||
|
# Pfeilbeschreibung:
|
||||||
|
if support_name == "A":
|
||||||
|
txt_alignment = "left"
|
||||||
|
txt_x_offset = -120
|
||||||
|
name_x_offset = -50
|
||||||
|
name_y_offset = 15
|
||||||
|
transverse_force = height_1.transverse_force_support_a
|
||||||
|
else:
|
||||||
|
txt_alignment = "left"
|
||||||
|
txt_x_offset = 20
|
||||||
|
name_x_offset = 20
|
||||||
|
name_y_offset = 15
|
||||||
|
transverse_force = height_1.transverse_force_support_b
|
||||||
|
|
||||||
|
# Plottet Querkraft-Wert entweder am Festlager "A", oder am Loslager "B"
|
||||||
|
ax_drawing.annotate(f"F{support_name}y: {transverse_force:.0f}N", xy=(x_pos, y_beam_pos - arrow_length),
|
||||||
|
xycoords='data', xytext=(txt_x_offset, 0), textcoords='offset pixels', horizontalalignment=txt_alignment)
|
||||||
|
|
||||||
|
# Plottet Lagerbezeichnung
|
||||||
|
ax_drawing.annotate(f"{support_name}", xy=(x_pos, y_beam_pos), xycoords='data', xytext=(name_x_offset, name_y_offset),
|
||||||
|
textcoords='offset pixels', ha=txt_alignment, fontsize=20)
|
||||||
|
|
||||||
|
|
||||||
|
def draw_fixed_support(y_beam_pos, px_amount_per_mm, arrow_length, support_name):
|
||||||
|
# Querkraft:
|
||||||
|
draw_transverse_force(x_pos=height_1.all_force_positions[0], y_beam_pos=y_beam_pos,
|
||||||
|
arrow_length=arrow_length, support_name=support_name)
|
||||||
|
|
||||||
|
# Normalkraft:
|
||||||
|
x_arrow_length = px_amount_per_mm * arrow_length
|
||||||
|
ax_drawing.annotate('', xycoords='data', xytext=(height_1.all_force_positions[0], y_beam_pos), xy=(-x_arrow_length, y_beam_pos),
|
||||||
|
textcoords='data', arrowprops=dict(color="red", width=1, headwidth=6))
|
||||||
|
|
||||||
|
|
||||||
|
def draw_couple(y_beam_pos, px_amount_per_mm, arrow_length, support_name):
|
||||||
|
# Feste Einspannung
|
||||||
|
draw_fixed_support(y_beam_pos=y_beam_pos, px_amount_per_mm=px_amount_per_mm, arrow_length=arrow_length, support_name=support_name)
|
||||||
|
|
||||||
|
# Plottet Querkraft-Pfeil entweder am Festlager "A", oder am Loslager "B"
|
||||||
|
ax_drawing.annotate(f"M1: {(height_1.support_bending_moment / 1000):.0f} Nm", xy=(height_1.all_force_positions[0], y_beam_pos - arrow_length),
|
||||||
|
xycoords='data', xytext=(-120, -20), textcoords='offset pixels', horizontalalignment="left")
|
||||||
|
|
||||||
|
ax_drawing.plot([height_1.all_force_positions[0] - 2.5], [y_beam_pos], marker=r'$\circlearrowright$', ms=30, color="red")
|
||||||
|
|
||||||
|
|
||||||
|
def drawing_dimensions(y_pos_beam, px_amount_per_mm, arrow_length):
|
||||||
|
dim_line_distance_from_beam = 70
|
||||||
|
dim_line_y_pos = y_pos_beam + dim_line_distance_from_beam
|
||||||
|
dim_leader_extension = 20
|
||||||
|
|
||||||
|
for i, (force_pos, force) in enumerate(zip(height_1.all_force_positions, height_1.all_forces())):
|
||||||
|
# Darstellung Maßhilfslinien
|
||||||
|
ax_drawing.vlines(force_pos, y_pos_beam, dim_line_y_pos + dim_leader_extension, colors="black", linewidth=1)
|
||||||
|
|
||||||
|
if height_1.has_two_supports:
|
||||||
|
end_index = len(height_1.all_force_positions) - 1
|
||||||
|
else:
|
||||||
|
end_index = len(height_1.all_force_positions)
|
||||||
|
|
||||||
|
if 0 < i < end_index:
|
||||||
|
# Kraftpfeile
|
||||||
|
# Pfeildarstellung
|
||||||
|
ax_drawing.annotate('', xycoords='data', xytext=(height_1.all_force_positions[i], beam_y_pos_in_plot + arrow_length),
|
||||||
|
xy=(height_1.all_force_positions[i], beam_y_pos_in_plot), textcoords='data',
|
||||||
|
arrowprops=dict(color="red", width=1, headwidth=6))
|
||||||
|
|
||||||
|
# Pfeilbeschreibung
|
||||||
|
ax_drawing.annotate(f'F{i}:\n{force:.1f}N', xy=(force_pos, y_pos_beam - arrow_length), xycoords='data', xytext=(0, 0),
|
||||||
|
textcoords='offset pixels', horizontalalignment="center", fontsize=10)
|
||||||
|
|
||||||
|
if i < (len(height_1.all_force_positions) - 1):
|
||||||
|
# Darstellung Maßlinien
|
||||||
|
ax_drawing.annotate('', xy=(height_1.all_force_positions[i], dim_line_y_pos), xycoords='data',
|
||||||
|
xytext=(height_1.all_force_positions[i + 1], dim_line_y_pos), textcoords='data', arrowprops={'arrowstyle': '<->'})
|
||||||
|
|
||||||
|
# Darstellung Maße (Bereichslängen)
|
||||||
|
section_length = (height_1.all_force_positions[i + 1] - force_pos)
|
||||||
|
dim_x_pos = (section_length / 2) / px_amount_per_mm
|
||||||
|
ax_drawing.annotate(f'{section_length:.1f}', xy=(force_pos, dim_line_y_pos), xycoords='data', xytext=(dim_x_pos, 5),
|
||||||
|
textcoords='offset pixels', horizontalalignment="center")
|
||||||
|
|
||||||
|
|
||||||
|
drawing_dimensions(y_pos_beam=beam_y_pos_in_plot, px_amount_per_mm=mm_in_px_converter, arrow_length=force_arrow_length)
|
||||||
|
|
||||||
|
if height_1.has_two_supports:
|
||||||
|
draw_transverse_force(x_pos=height_1.all_force_positions[len(height_1.all_force_positions) - 1], y_beam_pos=beam_y_pos_in_plot,
|
||||||
|
arrow_length=force_arrow_length, support_name="B")
|
||||||
|
|
||||||
|
draw_fixed_support(y_beam_pos=beam_y_pos_in_plot, px_amount_per_mm=mm_in_px_converter, arrow_length=force_arrow_length, support_name="A")
|
||||||
|
else:
|
||||||
|
draw_couple(y_beam_pos=beam_y_pos_in_plot, px_amount_per_mm=mm_in_px_converter, arrow_length=force_arrow_length, support_name="A")
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================================================================================
|
||||||
|
# = Höhe und Biegemoment =
|
||||||
|
# ===========================================================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
def text_alignment():
|
||||||
|
if height_1.has_two_supports:
|
||||||
|
return "center"
|
||||||
|
return "left"
|
||||||
|
|
||||||
|
|
||||||
|
def plot_bending_moment():
|
||||||
|
|
||||||
|
# Momentenlinie
|
||||||
|
ax_bending_moment.plot(height_1.all_force_positions, height_1.bending_moments, color="black")
|
||||||
|
ax_bending_moment.fill_between(height_1.all_force_positions, height_1.bending_moments, hatch="/", facecolor="#FFFFFF")
|
||||||
|
|
||||||
|
# Maximalmoment
|
||||||
|
bending_moments_and_beam_lengths = dict(zip(height_1.bending_moments, height_1.all_force_positions))
|
||||||
|
max_bending_moment = max(bending_moments_and_beam_lengths.keys())
|
||||||
|
len_at_max_bending_moment = bending_moments_and_beam_lengths[max_bending_moment]
|
||||||
|
text = f"max: {round(max_bending_moment, 2)} Nm"
|
||||||
|
|
||||||
|
ax_bending_moment.plot(len_at_max_bending_moment, max_bending_moment, "o", color="black")
|
||||||
|
|
||||||
|
ax_bending_moment.annotate(text, xy=(len_at_max_bending_moment, max_bending_moment), xytext=(0, 10), textcoords='offset points',
|
||||||
|
horizontalalignment=text_alignment())
|
||||||
|
|
||||||
|
|
||||||
|
def plot_heights():
|
||||||
|
|
||||||
|
# Hauptplot
|
||||||
|
ax_heights.plot(height_1.all_force_positions, height_1.heights, color="black",
|
||||||
|
label=f"{height_1.cross_section_type} - Breite: {height_1.cross_section_width} mm")
|
||||||
|
ax_heights.fill_between(height_1.all_force_positions, height_1.heights, hatch="/", facecolor="#FFFFFF")
|
||||||
|
|
||||||
|
# Plot Maximalhöhe
|
||||||
|
heights_and_beam_lengths = dict(zip(height_1.heights, height_1.all_force_positions))
|
||||||
|
max_height = max(heights_and_beam_lengths.keys())
|
||||||
|
beam_length_at_max_height = heights_and_beam_lengths[max_height]
|
||||||
|
text = f"max: {max_height:.2f} mm"
|
||||||
|
|
||||||
|
ax_heights.plot(beam_length_at_max_height, max_height, "o", color="black")
|
||||||
|
ax_heights.annotate(text, xy=(beam_length_at_max_height, max_height), xytext=(0, 10), textcoords='offset points',
|
||||||
|
horizontalalignment=text_alignment())
|
||||||
|
|
||||||
|
ax_heights.legend(loc=1)
|
||||||
|
|
||||||
|
|
||||||
|
def update_slider_length(current_length):
|
||||||
|
ax_heights.clear()
|
||||||
|
ax_bending_moment.clear()
|
||||||
|
|
||||||
|
plot_heights()
|
||||||
|
current_height = round(height_1.current_value(current_length, height_1.heights), 2)
|
||||||
|
current_bending_moment = round(height_1.current_value(current_length, height_1.bending_moments), 2)
|
||||||
|
|
||||||
|
# Plot aktuelle Höhe
|
||||||
|
plot_bending_moment()
|
||||||
|
ax_heights.plot(current_length, current_height, "o", color="black")
|
||||||
|
ax_heights.annotate(current_height, xy=(current_length, current_height), xytext=(0, 7), textcoords='offset points',
|
||||||
|
horizontalalignment="center")
|
||||||
|
|
||||||
|
# Plot aktuelles Moment
|
||||||
|
ax_bending_moment.plot(current_length, current_bending_moment, "o", color="black")
|
||||||
|
ax_bending_moment.annotate(current_bending_moment, xy=(current_length, current_bending_moment), xytext=(0, 7), textcoords='offset points',
|
||||||
|
horizontalalignment="center")
|
||||||
|
|
||||||
|
ax_heights.vlines(current_length, 0, max(height_1.heights) * 1.25, color="gray", linewidth=1)
|
||||||
|
ax_bending_moment.vlines(current_length, 0, max(height_1.bending_moments) * 1.25, color="gray", linewidth=1)
|
||||||
|
|
||||||
|
plt.draw()
|
||||||
|
|
||||||
|
|
||||||
|
plot_bending_moment()
|
||||||
|
plot_heights()
|
||||||
|
|
||||||
|
ax_slider = plt.axes([0.125, 0.0025, 0.775, 0.05], facecolor="blue")
|
||||||
|
slider = Slider(ax_slider, "Trägerlänge", valmin=0, valmax=height_1.beam_length, valstep=1, valinit=0)
|
||||||
|
slider.on_changed(update_slider_length)
|
||||||
|
|
||||||
|
plt.show()
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
import argparse
|
|
||||||
from matplotlib import pyplot as plt
|
|
||||||
from math import floor
|
|
||||||
|
|
||||||
|
|
||||||
class ProfileHeights:
|
|
||||||
|
|
||||||
bobbin_train_segment_types = {165: 0.38, 210: 0.248, 225: 0.354648} # Key = Spulenzug-Typ - Value = Gewicht
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
cross_section_type,
|
|
||||||
datum,
|
|
||||||
width,
|
|
||||||
thickness,
|
|
||||||
ap_positions=None,
|
|
||||||
beam_length=2000,
|
|
||||||
min_ap_distance=200,
|
|
||||||
yield_strength=235,
|
|
||||||
segment_type=165,
|
|
||||||
bobbin_mass=4,
|
|
||||||
column_distance=3.6,
|
|
||||||
):
|
|
||||||
|
|
||||||
self.cross_section_type = cross_section_type
|
|
||||||
self.datum = datum
|
|
||||||
self.width = width
|
|
||||||
self.thickness = thickness
|
|
||||||
self.ap_positions = ap_positions
|
|
||||||
self.beam_length = beam_length
|
|
||||||
self.min_ap_distance = min_ap_distance
|
|
||||||
self.yield_strength = yield_strength
|
|
||||||
self.segment_type = segment_type
|
|
||||||
self.bobbin_mass = bobbin_mass
|
|
||||||
self.column_distance = column_distance
|
|
||||||
|
|
||||||
def ap_position_check(self):
|
|
||||||
# Überprüft, ob alle AP's den zulässigen Mindestabstand zueinander einhalten
|
|
||||||
|
|
||||||
if not [abs(new_pos - old_pos) < self.min_ap_distance for old_pos, new_pos in zip(self.ap_positions, self.ap_positions[1:])]:
|
|
||||||
raise ValueError(f"Abstände zwischen ")
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def ap_amount(self):
|
|
||||||
return floor(self.beam_length / self.min_ap_distance)
|
|
||||||
|
|
||||||
def individual_ap_positions(self):
|
|
||||||
# Funktion gibt Einzelabstände der AP-Profile zur Säule zurück
|
|
||||||
|
|
||||||
# Fall 1: Es wurden keine Einzelabstände zur Säule definiert. Abstand zwischen AP's = zulässiger Mindestabstand
|
|
||||||
# Fall 2: Es wurden individuelle AP-Abstände definiert.
|
|
||||||
|
|
||||||
if self.ap_positions is None:
|
|
||||||
return [ap_count * self.min_ap_distance for ap_count in range(1, self.ap_amount() + 1)]
|
|
||||||
elif self.ap_position_check():
|
|
||||||
return self.ap_positions
|
|
||||||
|
|
||||||
def force_per_ap_meter(self):
|
|
||||||
if self.bobbin_mass > 4:
|
|
||||||
raise ValueError(f"Gewicht der Spulen darf max. 4 kg betragen!")
|
|
||||||
else:
|
|
||||||
column_rows = 2
|
|
||||||
location_factor = 9.81 # m/s^2
|
|
||||||
ap_weight_per_meter = 1.88
|
|
||||||
|
|
||||||
total_bobbin_mass = self.bobbin_mass * (1000 / self.segment_type)
|
|
||||||
mass = total_bobbin_mass + 2 * self.bobbin_train_segment_types[self.segment_type] + ap_weight_per_meter
|
|
||||||
|
|
||||||
return mass * location_factor * self.column_distance / column_rows
|
|
||||||
|
|
||||||
def ref_moment_of_resistance(self, current_beam_length, ap_column_distances, force_per_rail):
|
|
||||||
# Funktion berechnet Widerstandsmoment für die aktuelle Trägerlänge
|
|
||||||
|
|
||||||
total_column_distance = 0
|
|
||||||
for ap_column_distance in ap_column_distances:
|
|
||||||
if current_beam_length >= ap_column_distance:
|
|
||||||
total_column_distance += ap_column_distance
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
safety_factor = 1.7
|
|
||||||
bend_moment = force_per_rail * total_column_distance
|
|
||||||
allowed_bend_stress = (self.yield_strength * 1.2) / safety_factor
|
|
||||||
|
|
||||||
return bend_moment / allowed_bend_stress
|
|
||||||
|
|
||||||
def recalculated_moment_of_resistance(self, height):
|
|
||||||
# Berechnet das Widerstandsmoment mit der derzeitigen Profilhöhe für verschiedene Querschnitts-Arten
|
|
||||||
|
|
||||||
inner_width = self.width - 2 * self.thickness
|
|
||||||
inner_height = height - 2 * self.thickness
|
|
||||||
bar_width = self.width - self.thickness
|
|
||||||
|
|
||||||
# 1. Widerstandsmoment-Berechnung für idealisiertes rechteckiges Hohlprofil:
|
|
||||||
if self.cross_section_type == "hohlprofil":
|
|
||||||
return (self.width * height ** 3 - inner_width * inner_height ** 3) / (6 * height)
|
|
||||||
|
|
||||||
# 2. Widerstandsmoment-Berechnung für idealisiertes IPE-Profil / C-Profil:
|
|
||||||
elif self.cross_section_type in ["c", "ipe"]:
|
|
||||||
return (self.width * height ** 3 - bar_width * inner_height ** 3) / (6 * height)
|
|
||||||
|
|
||||||
def datum_reference(self):
|
|
||||||
# Legt fest, ob die notwendige Profilhöhe im Plot in Abhängigkeit von jedem Millimeter der Gesamtlänge [l]
|
|
||||||
# des Trägers oder der Trägerlänge an der Stelle jedes AP-Profils [n] dargestellt werden soll.
|
|
||||||
|
|
||||||
if self.datum == "l":
|
|
||||||
return list(range(self.min_ap_distance, self.beam_length + 1))
|
|
||||||
|
|
||||||
elif self.datum == "n":
|
|
||||||
return self.individual_ap_positions()
|
|
||||||
|
|
||||||
def height_calculation(self):
|
|
||||||
|
|
||||||
start_height = 2 * self.thickness
|
|
||||||
|
|
||||||
beam_lengths = self.datum_reference()
|
|
||||||
ap_column_distance = self.individual_ap_positions()
|
|
||||||
force_per_ap_meter = self.force_per_ap_meter()
|
|
||||||
|
|
||||||
heights = []
|
|
||||||
lengths = []
|
|
||||||
moment_of_resistances = []
|
|
||||||
recalculated_moment_of_resistance = 0
|
|
||||||
|
|
||||||
for count, beam_length in enumerate(beam_lengths):
|
|
||||||
reference_moment_of_resistance = self.ref_moment_of_resistance(beam_length, ap_column_distance, force_per_ap_meter)
|
|
||||||
|
|
||||||
while recalculated_moment_of_resistance < reference_moment_of_resistance:
|
|
||||||
recalculated_moment_of_resistance = self.recalculated_moment_of_resistance(start_height)
|
|
||||||
start_height += 0.01
|
|
||||||
|
|
||||||
moment_of_resistances.append(reference_moment_of_resistance)
|
|
||||||
heights.append(start_height)
|
|
||||||
lengths.append(beam_length)
|
|
||||||
|
|
||||||
return heights, lengths
|
|
||||||
|
|
||||||
|
|
||||||
a = ProfileHeights(cross_section_type="hohlprofil", datum="l", width=50, thickness=2.5)
|
|
||||||
|
|
||||||
ya_heights, xa_lengths = a.height_calculation()
|
|
||||||
plt.plot(xa_lengths, ya_heights)
|
|
||||||
|
|
||||||
plt.xlabel("Länge des Trägers in mm")
|
|
||||||
plt.ylabel("Benötigte Querschnittshöhe des Trägers in mm")
|
|
||||||
plt.grid(True)
|
|
||||||
plt.show()
|
|
||||||
Reference in New Issue
Block a user