heute mal wieder was passendes zum Forum, ich würde euch gerne mal den Zwischenstand meines Programms zur Schweißmaschine zeigen. Geschrieben wurde es in MicroPython mit dieser Version. Die benötige ich wegen dem Counter und MCPWM, das @__deets__ eingebaut hat.
Ich habe mal ein paar Tests gemacht und so weit ich das überblicken konnte funktioniert der „Hauptteil“ auch. Da es absolut nicht ausgeschlossen ist, dass meine Programmstruktur falsch oder nicht besonders sinnvoll ist und da ich sicherlich auch einige schlechte Codezeilen geschrieben habe, würde ich euch gerne mal drüber schauen lassen.
Die Ausgabe über das Display fehlt noch, da habe ich noch keinen wirklichen Plan wie ich das machen soll, dazu später mehr.
Ich habe neulich ein Bild der Maschine hier gepostet, aber hier habe ich das ganze noch mal schematisch dargestellt:
https://www.dropbox.com/s/oa7i93isf0skc ... 3.png?dl=0
Erst mal die Funktionsbeschreibung in Textform:
- Maschine einschalten:
- Keine Aktion bevor nicht mit „Start“ bestätigt wird
- Referenzfahrt der Maschine
- Abfrage der Endlagen, wenn die linke Endlage nicht aktiv ist, dreht der Schwenkmotor bis
bis sie aktiv ist. Motor hält an, Pneumatikzylinder fährt aus. Kurze Pause um sicher zu
stellen dass der Zylinder ausgefahren ist.
- Abfrage des Näherungsschalters. Ist er nicht aktiv, drehen der Schweissachse bis der Näherungsschalter das Loch in der Scheibe gefunden hat. Da die Scheibe nicht direkt auf der
Schweissachse ist, kann der Benutzer durch erneutes „Start“ drücken den Motor eine weitere
Umdrehung drehen lassen um die Startposition festzulegen. Wird innerhalb von 5s „Start“
nicht gedrückt, wird der Encoder auf 0 gesetzt
- Maschinenreferenzfahrt abgeschlossen
- Status LED an
- „Start“-Taster betätigt:
- Status LED ausfahren
- Schweißen darf nur starten wenn die Endlage betätigt ist
Wenn Simulations-Modus Schalter auf 1 steht, darf das Schweißgerät nicht angehen
- Wenn Simulations-Modus auf 0 steht Schweißgerät einblenden
- Warten bis eingestellte Verweilzeit abgelaufen ist
- Abhängig von der belegten Endlage die Schweßachse in entsprechende Richtung drehen
- Wenn eingestellter Drehwinkel erreicht ist, Schweißachse und Schweißgerät stoppen
- Schweißachse auf Ausgangsposition zurück fahren.
Wenn Automatik-Modus auf 1 steht, Zylinder einfahren, kurz warten, schwenken bis Endlage erreicht wird, anderer Pneumatikzylinder ausfahren, kurz warten, Schweißprozess wiederholen, nach Schweißprozess auf Ausgangsstellung zurück schwenken. Status LED an.
Wenn Automatik-Modus auf 0 steht, Status LED an.
- Während des Schweißprozesses muss der Endlagenschalter immer abgefragt werden. Wenn er nicht mehr aktiv ist, Schweißprozess abbrechen.
- „Schwenk“-Taster bestätigt:
- Pneumatikzylinder einfahren.
- Schwenkmotor dreht auf Endlagen
- Pneumatikzylinder ausfahren
- Kurz warten
- Status LED an
- „Geschwindigkeit“-Poti:
- Gibt die Drehzahl der Schweißachse vor.
- Einheit ist Prozent
- Soll während des Schweißens änderbar sein
- „Winkel“-Poti:
- Gibt den Drehwinkel vor um die sich die Schweißachse drehen soll
- Einheit in Grad
- Soll während des Schweißens änderbar sein
- „Zeit“-Poti:
- Gibt die Verweilzeit vor, in der der Schweißbrenner an ist, die Schweißachse aber noch steht
- Einheit in Sekunden
- Soll während des Wartens änderbar sein
- Fehler Endlage verloren
- Schweißen abbrechen
- Keine weiteren Aktionen anwählbar
- Mit „Start“-Taster erneute Referenzfahrt bestätigen.
- Sicherstellen das die Pneumatikzylinder eingefahren sind
- Start Referenzfahrt
Ich habe noch nie eine Ablaufsteuerung geschrieben und weis gar nicht wie man das sinnvoll aufbauen könnte. Bei mir steckt jetzt alles in einer Klasse und das sieht so aus:
Code: Alles auswählen
from time import sleep, ticks_diff, ticks_us
from esp32 import MCPWM, PCNT
from LargeCounter import LargeCounter
from machine import ADC, Pin
ENCODER_PINS = [2, 15]
REFERENCE_SWITCH_PIN = 4
END_POSITION_PINS = {"left": 18, "right": 19}
AUTOMATIC_BUTTON_PIN = 35
SIMULATE_BUTTON_PIN = 33
START_BUTTON_PIN = 25
ROTATE_BUTTON_PIN = 21
TURN_SWITCH_PINS = {"wait_for_start": 34, "speed": 36, "welding_angle": 39}
STATUS_LED_PIN = 32
WELDING_MACHINE_PIN = 26
PNEUMATIC_CYLINDER_PINS = {"left": 22, "right": 27}
PWM_PINS = {"welding_axis": [5, 16], "rotation_axis": [17, 23]}
ENCODER_RESOLUTION = 5000
# Welding-angle in °, speed in %, time before start welding in s
MAX_VALUES_TO_TRANSLATE_DIGITAL_VALUE = [450, 100, 5]
POSITION_MACHINE_START = "left"
RATIO = 96 / 41
class WeldingControl:
def __init__(self):
self.turn_switches = {
actuator: ADC(Pin(TURN_SWITCH_PINS[actuator]))
for actuator in TURN_SWITCH_PINS
}
for turn_switch, pin in self.turn_switches.items():
pin.atten(self.turn_switches[turn_switch].ATTN_11DB)
self.start = Pin(START_BUTTON_PIN, Pin.IN, Pin.PULL_DOWN)
self.rotate_workpiece = Pin(ROTATE_BUTTON_PIN, Pin.IN, Pin.PULL_DOWN)
self.simulate_mode = Pin(SIMULATE_BUTTON_PIN, Pin.IN, Pin.PULL_DOWN)
self.automatic_mode = Pin(AUTOMATIC_BUTTON_PIN, Pin.IN, Pin.PULL_DOWN)
self.welding_axis = MCPWM(0)
self.welding_axis.bind(
Pin(PWM_PINS["welding_axis"][0]), Pin(PWM_PINS["welding_axis"][1])
)
self.turning_axis = MCPWM(3)
self.turning_axis.bind(
Pin(PWM_PINS["rotation_axis"][0]), Pin(PWM_PINS["rotation_axis"][1])
)
self.welding_machine = Pin(WELDING_MACHINE_PIN, Pin.OUT)
self.cylinders = {
position: Pin(pin, Pin.OUT)
for position, pin in PNEUMATIC_CYLINDER_PINS.items()
}
self.status_led = Pin(STATUS_LED_PIN, Pin.OUT)
self.machine_start_reference = Pin(
POSITION_MACHINE_START, Pin.IN, Pin.PULL_DOWN
)
self.end_positions = {
position: Pin(END_POSITION_PINS[position], Pin.IN)
for position in END_POSITION_PINS
}
encoder = PCNT(0, Pin(ENCODER_PINS[0]), Pin(ENCODER_PINS[1]))
self.counter = LargeCounter(encoder)
try:
self.active_end_position = [
position
for position in self.end_positions
if not self.end_positions[position].value()
][0]
except IndexError:
self.active_end_position = None
self.welding_axis.duty(0)
self.status_led.off()
for cylinder in self.cylinders.values():
cylinder.off()
sleep(1)
self.wait_for_initial()
@property
def actually_welding_position(self):
# position in angle
return (
self.direction * 360 * self.counter.counter() // ENCODER_RESOLUTION // RATIO
)
@property
def direction(self):
return 1 if self.active_end_position == POSITION_MACHINE_START else -1
@property
def time_before_start(self):
return self.translate_measure_value(
self.turn_switches["wait_for_start"].read(),
MAX_VALUES_TO_TRANSLATE_DIGITAL_VALUE[2],
)
@property
def welding_angle(self):
return self.translate_measure_value(
self.turn_switches["welding_angle"].read(),
MAX_VALUES_TO_TRANSLATE_DIGITAL_VALUE[0],
)
@property
def welding_speed(self):
return self.translate_measure_value(
self.turn_switches["speed"].read(),
MAX_VALUES_TO_TRANSLATE_DIGITAL_VALUE[1] * self.direction,
)
@staticmethod
def translate_measure_value(value, reference_value):
# 4095 = max. value ADC.read()
if reference_value > 10:
return reference_value - (reference_value * value // 4095)
else:
return round(reference_value - (reference_value * value / 4095), 1)
def check_machine_position(self):
self.turning_axis.duty(50)
if not self.end_positions[POSITION_MACHINE_START].value():
self.turning_axis.duty(0)
self.active_end_position = POSITION_MACHINE_START
return True
def control_cylinder(self, state, position):
if state == "out":
self.cylinders[position].on()
else:
self.cylinders[position].off()
def control_welding_process(self):
self.status_led.off()
self.do_welding()
self.stop_welding()
if self.automatic_mode.value():
self.control_workpiece_rotation()
sleep(1)
self.do_welding()
self.stop_welding()
self.control_workpiece_rotation()
self.status_led.on()
def control_workpiece_rotation(self):
self.control_cylinder("in", self.active_end_position)
sleep(1)
self.rotate()
sleep(1)
self.control_cylinder("out", self.active_end_position)
def do_welding(self):
if self.end_positions[self.active_end_position].value():
self.stop_welding()
self.set_back_to_initial_mode()
return
if not self.simulate_mode.value():
self.welding_machine.on()
start = ticks_us()
while True:
if self.end_positions[self.active_end_position].value():
self.stop_welding()
self.set_back_to_initial_mode()
break
if ticks_diff(ticks_us(), start) > self.time_before_start * 1e6:
break
sleep(0.01)
self.welding_axis.duty(self.welding_speed)
while True:
if self.end_positions[self.active_end_position].value():
self.stop_welding()
self.set_back_to_initial_mode()
break
if self.actually_welding_position >= self.welding_angle:
break
self.welding_axis.duty(self.welding_speed)
sleep(0.01)
def initial_machine(self):
if self.active_end_position is None:
self.control_cylinder("in", "left")
self.control_cylinder("in", "right")
sleep(1)
while True:
if self.check_machine_position():
sleep(0.5)
self.control_cylinder("out", self.active_end_position)
sleep(1)
break
sleep(0.1)
else:
self.control_cylinder("out", self.active_end_position)
while not self.initial_welding_axis():
pass
def initial_welding_axis(self):
initial_status = None
self.welding_axis.duty(100)
while True:
if self.machine_start_reference.value():
self.welding_axis.duty(0)
timestamp = ticks_us()
while True:
if ticks_diff(ticks_us(), timestamp) >= 5e6:
initial_status = True
break
if self.start.value():
self.welding_axis.duty(100)
sleep(0.5)
initial_status = False
break
if initial_status:
self.counter.reset()
return True
sleep(0.01)
def monitor_user_input(self):
while True:
if self.simulate_mode.value():
self.welding_machine.off()
if self.start.value():
self.switching_start_stop()
if self.rotate_workpiece.value() and self.status_led.value():
self.status_led.off()
self.control_workpiece_rotation()
sleep(1)
self.status_led.on()
sleep(0.1)
def rotate(self):
for end_position in self.end_positions:
if self.active_end_position != end_position:
break
self.turning_axis.duty(
50 if self.active_end_position == POSITION_MACHINE_START else -50
)
sleep(1)
while True:
if not self.end_positions[end_position].value():
self.turning_axis.duty(0)
self.active_end_position = end_position
break
sleep(0.1)
def set_back_to_initial_mode(self):
self.status_led.off()
self.cylinders[self.active_end_position].off()
self.active_end_position = None
self.wait_for_initial()
def stop_welding(self):
self.welding_axis.duty(0)
self.welding_machine.off()
sleep(1)
if self.actually_welding_position != 360:
self.welding_axis.duty(-60 * self.direction)
while True:
if self.actually_welding_position in [360, 0]:
self.welding_axis.duty(0)
break
self.counter.reset()
def switching_start_stop(self):
if not self.status_led.value():
self.stop_welding()
self.status_led.on()
else:
self.control_welding_process()
def wait_for_initial(self):
while True:
if self.start.value():
self.initial_machine()
self.status_led.on()
break
sleep(0.1)
self.monitor_user_input()
def main():
WeldingControl()
if __name__ == "__main__":
main()
Das Display will ich nur aktualisieren wenn sich ein Wert geändert hat. Also die drei Einstellwerte mit den Potis. Eventuell kommen noch ein zwei Hinweise dazu, zum Beispiel "Für Referenzfahrt Start drücken" oder so.
Macht es Sinn, dafür eine eigene Klasse zu schreiben und für die Aktualisierung und Abfrage einen Thread zu starten?
Wie sortiert man so ein Programm eigentlich? Ich habe mal mit dem Eigenschaften angefangen und die Funktionen alphabetisch.
Wer das jetzt bis hier her alles gelesen hat, vielen Dank dafür schon mal!
Falls ihr mal Zeit und Lust habt würde ich mich über jede Kritik und Verbesserung freuen.
Viele Grüße
Dennis
Edit: Die Endpositionen muss ich mit 'not' abfragen, da die 0 liefer wenn sie betätigt sind. Das muss ich auf dem Steckbrett noch ändern.