Andu Posted April 29, 2014 at 10:09 AM Posted April 29, 2014 at 10:09 AM Hallo, ich arbeite gerade an einer Art 3-Achs Portalroboter für ein Probenhandling. Dabei wird für jede Achse mit einem Schrittmotor über eine Gewindespindel auf einer Linearführung verschoben, dass ganze wird mit einem Stepperbrick gesteuert. Ich habe also einen Stapel aus PowerSupply, darüber einem Master und dann drei Stepper Bricks. Am Master sind noch zwei Joysticks und an jeweils einem Stepper ein I/O Bricklet um die Endschalter auszulesen. Die Programmierung funktioniert soweit ich habe nur ein Problem: Wenn ich alle drei Motoren gleichzeitig in einer Schleife immer wieder hin und her fahren lasse (sollte ein Dauertest werden, ob irgendetwas warm wird) bewegt sich das Portal beim ersten Durchlauf wie gewünscht. Danach bleibt immer eine Achse stehen oder fährt nicht genügend Schritte. Welche Achse das ist ist leider nicht reproduzierbar. Hier mal der dazugehörige Programmcode: For n = 0 To 5 Portal.Bewege(Länge_X / 10, 20000, Länge_Y / 10, 20000, Länge_Z / 10, 20000) Do While Portal.inBewegung = True Application.DoEvents() Loop 'MsgBox("hin") Threading.Thread.Sleep(500) Portal.Bewege(0, 20000, 0, 20000, 0, 20000) Do While Portal.inBewegung = True Application.DoEvents() Loop 'MsgBox("zurück") Threading.Thread.Sleep(500) Next zur Erklärung: Portal.Bewege ist selbst geschrieben und übergibt die Anzahl der Schritte und die Geschwindigkeit an den jeweiligen Motor, das ganze für alle drei Achsen. Portal.inBewegung wird über die Callbacks gesteuert und wird auf False gesetz sobald alle Achsen ihre Zielposition erreicht haben. Wenn ich wie im Beispiel eine MassageBox einbaue läuft alles wie gewollt. Die Pause zwischen den Schritten lös das Problem allerdings nicht. Habe ich jetzt Programm technisch etwas falsch gemacht, oder kommt der Master mit der Kommunikation mit den Steppern nicht hinterher? Vielen Dank fürs lesen und für eure Tipps Andu Quote
photron Posted April 30, 2014 at 10:19 AM Posted April 30, 2014 at 10:19 AM Die MassageBox erzeugt eine Pause im Programmablauf und führt auch ihren eigenen Message-Loop aus. Hast du mal einen deutlich längeren Sleep als 500ms probiert? Hast du mal vor und nach dem Sleep versucht jeweils einmal Application.DoEvents() aufzurufen? Quote
Andu Posted May 4, 2014 at 02:46 PM Author Posted May 4, 2014 at 02:46 PM Das längste was ich getestet hab waren 1000ms, werde morgen mal etwas längeres testen. Nein hatte bishern ur ein DoEvents in der Schleife, auch das werde ich morgen früh gleich testen. Danke schonmal Update: So 5sec Pause und ein DoEvents vor und nachher ändern auch nichts an meinem Problem... Quote
Nic Posted May 5, 2014 at 01:30 PM Posted May 5, 2014 at 01:30 PM Ev. würde ich den prozeduralen Ansatz verlassen, die Loop und sleeps weglassen. Und auf den Callback OnPositionReached jedes Steppers erst reagieren und mittels internem counter die Wiederholungen kontrollieren. Quote
Andu Posted May 7, 2014 at 08:00 AM Author Posted May 7, 2014 at 08:00 AM Mit Portal.inBewegung frage ich genau diese Callbacks ab, der Wert wird nur True wenn alle Motoren ihre Position erreicht haben. Die Sleeps sind da auch nur aus Verzweiflung drin, falls einer von den Callbacks verschluckt wird oder so. Eigentlich wollte ich die leere Schleife durchlaufen bis dieser Wert True ist. Quote
Nic Posted May 8, 2014 at 07:13 AM Posted May 8, 2014 at 07:13 AM Ich würde das anders lösen: Mache 4 private Variablen in der Anwendung, einen int als n, und jeweils einen Boolean Motor_Stopped<X oder Y oder Z> für jeden Motor. In einer Startproc(zB. Button Click) beginne mit n=1 und alle Booleans auf false und rufe dort Portal.Bewege auf. Im jeweiligen OnPositionReached: Der Boolean wird auf true gesetzt. Dann prüfst du die beiden anderen Booleans, wenn die auch True sind, inkrementiere n um 1, falls n<=5 löst du Portal.Bewege erneut aus, sonst nichts mehr. Quote
Andu Posted May 8, 2014 at 09:36 AM Author Posted May 8, 2014 at 09:36 AM @Nic: Genau das mache ich in den Motorklassen, ich habe für jeden Motor eine Variable Motor.inBewegung, für Portal.inBewegung werde die einfach or verknüpft. Mein Problem ist auch nicht das die Schleife nicht 5 mal ausgeführt wird, sondern das die Achsen sich nicht gleichzeitig bewegen sondern nacheinander. Das ist im ersten Posting wohl nicht ganz deutlich geworden sorry. Also alle Achsen bewgen sich 5 mal hin und her aber halt nicht gleichzeitig. Quote
Nic Posted May 8, 2014 at 11:19 AM Posted May 8, 2014 at 11:19 AM Was verstehst Du unter gleichzeitig ? Ein Befehl soll alle 3 Motoren gleichzeitig anlaufen lassen ? Parallelverarbeitung ist mit TF Teilen nicht möglich, im (theoretisch) günstigsten Fall wird jeder Befehl nacheinander an die Bricks im Abstand von 1ms abgearbeitet. Mein Ansatz hat übrigens keine Loop. Du aber zwingst den Hauptthread der GUI in der Schleife solange bis die Callbacks die Events bekommen wenn ich deinen Code richtig lese. Quote
Andu Posted May 8, 2014 at 02:32 PM Author Posted May 8, 2014 at 02:32 PM Mit gleichzeitig meine ich nicht parallel, die 1ms stört micht nicht. Im Moment bewegt sich zB. die Y-Achse erst wenn die X-Achse an ihrer Zielposition angekommen ist. Obwohl alle drei Achsen nacheinander angesprochen werden. Ja du liest den Code richtig, nachher in der Anwendung muss ich auf Daten von Messgeräten warten und dann mit der Achse wieder verfahren, da ich aber je nach Probe zu einer anderen Position muss kann ich das nicht über ein Event machen (glaube ich). Ich verstehe bei deinem Vorschlag nicht genau wie du den Code in eine erneute Prüfung der If-Abfrage zwingst wenn er einmal da angekommen ist. Quote
JoergK Posted May 9, 2014 at 05:05 AM Posted May 9, 2014 at 05:05 AM Erstmal kurz, da ich unterwegs zur Arbeit bin. Schau dir mal delegates und invokerequired an. dann erkläre mal, was dein code da oben macht. Damit meine ich nicht, was hinter den methodenaufrufen steckt. Sondern was der code genau macht. Stichwort schleife und dieses dämliche doevents. Dann wirst du wohl verstehen, was daran nicht so toll ist und warum es zu großer wahrscheinlichkeit zu fehlverhalten kommt. just my 2 cents. Ja ja Gemeinde, ich werde im Laufe des Tages mehr dazu sagen. Aber erstmal sollte man Nachdenken was man macht... Quote
JoergK Posted May 9, 2014 at 12:54 PM Posted May 9, 2014 at 12:54 PM Du erstellst Schleifen, höchstwahrscheinlich auf dem Hauptthread (den man niemals für soetwas nutzen sollte), die quasi dauerhaft den Prozessor blockieren. Dieses Relikt aus alten VB6 Zeiten (Application.DoEvents) sollte man wenn überhaupt extrem sparsam einsetzen und nicht in einer Warteschleife verwenden. Anstatt dem DoEvents hätte hier ein Thread.Sleep eher gepasst. irgendwas sinnvolles, je nachdem was du an Laufzeit erwartest. Wenn deine Steuerung 2 Minuten läuft bis die Endposition erreicht ist, sind 50ms übertrieben. Den Hauptthread alle 200ms oder 250ms schlafen zu legen wäre da eher gut. Kommt halt sehr drauf an. Wie gesagt, dein Hauptthread ist ständig damit beschäftigt, DoEvents auszuführen und blockiert mal gerne alles andere. Das .NET Framework muss man nicht darauf hinweisen, Hintergrund-Threads auszuführen. Das kann dies viel besser und schlauer von alleine. Also Finger weg von DoEvents (CompactFramework < 3.5 mal abgesehen, aber auch da sparsam). So nun zu einer besseren Lösung. Man legt ein Delegate an sowie ein Event. Der Hauptthread bindest du dann (Observer-Pattern) an das Event. Wenn du in deiner Steuerung den Wert "True" erreichst, setzt du nicht die Variable sondern feuerst das Event. Der Hauptthread wird informiert, aber vorsicht, wenn du deine Steuerung in einem anderen Thread hast, wird das Ziel des Events auf diesem ausgeführt. Der Backgroundthread darf aber keine GUI-Elemente verändern. Also mit Invoke-Required prüfen und über delegate auf GUI-Thread ausführen lassen. => GUI ist während der Laufzeit deiner Steuerung ansprechbar und keine lästigen und unnötigen Warteschleifen. Fazit: Thread.Sleep in Schleifen wenn du nicht viel ändern möchtest, Events und delegates, wenn du es richtig machen willst. Cheers, Jörg Quote
Andu Posted May 10, 2014 at 08:23 PM Author Posted May 10, 2014 at 08:23 PM Hi Jörg, danke erstmal für die Tipps, ich hab zwar noch keine Ahnung was du mir sagen willst aber ich denke mit den Stichworten und Google, MSDN und ein paar Bücher komm ich da hoffentlich schon weiter... Das DoEvents für .NET schlecht sind weiß ich das war auch eigentlich mehr Verzweiflung. Das mit den Invokes hab ich schon gemacht um auf dem GUI einen Radio Button anzusprechen der mit anzeigt ob sich das Portal noch bewegt. Jetzt nochmal für mein Verständnis: Du meinst also das meine Schleife den Hauptthread blockiert (wegen DoEvents) und deswegen der Hauptthread sozusagn das Event "Position Reached" der einzelnen Motoren verpasst? Gruß Andu Quote
JoergK Posted May 13, 2014 at 07:52 AM Posted May 13, 2014 at 07:52 AM Hallo, ja so sehe ich das von den Code-Ausschnitt. Durch diese Schleifen belegst du dauerhaft den Thread, in dem die Schleife ausgeführt wird. Wenn du nun die schlechtere Alternative zum Testen nehmen möchtest, legt den Thread kurz schlafen. Do While Portal.inBewegung = True Threading.Thread.Sleep(500) Loop MsgBox("hin") ... Dann hat ein anderer Thread die Möglichkeit, in den 500ms etwas zu tun. Besser ist, wenn du im "Portal" statt der boolschen Variable ein Event erstellst. public Event IchHabeFertig Wenn du dann an den Punkt kommst, wo du die Variable inBewegung setzt, einfach ein RaiseEvent IchHabeFertig(). Auf der GUI musst du dann "nur" noch einen Handler auf das Event vom Portal setzen: AddHandler Portal.IchHabeFertig, AddressOf MeineMethodeUmZuReagieren Sehr kurz beschrieben, aber Google gibt die viele Treffer bezüglich Event-Handling etc. Der theoretische Hintergrund ist das Entwicklungsmuster Observer. Cheers, Jörg Quote
Andu Posted June 16, 2014 at 05:09 AM Author Posted June 16, 2014 at 05:09 AM So um das Thema hier mal zu schließen meine Lösung die funktioniert (ganz ohne Sleeps und DoEvents^^) zum starten des ganzen: i = 0 Portal.Bewege(Länge_X, 20000, Länge_Y, 20000, Länge_Z, 20000) schickt das Portal los und aktiviert den Callback im Event wird dann Entschieden in welche Richtung gefahren werden soll und wie oft noch gefahren wird: If Portal.inBewegung = False And i < 20 Then If Position = "Anfang" Then Position = "Ende" i += 1 Portal.Bewege(0, 20000, 0, 20000, 0, 20000) Else Position = "Ende" i += 1 Portal.Bewege(Länge_X, 20000, Länge_Y, 20000, Länge_Z, 20000) End If End If jetzt kann der Dauertest kommen. Danke für eure Hilfe Quote
JoergK Posted June 18, 2014 at 12:26 PM Posted June 18, 2014 at 12:26 PM Erstmal hört sich das doch gut an. Freut mich dass es mit den Events klappt Wenn es interessiert, dann hab ich noch ein paar Anmerkungen zum Quellcode allgemeinerer Natur. Bspw. If Portal.inBewegung = False oder schlimmer If Portal.inBewegung = True sollte man niemals schreiben. Wie sagte das einer meiner Professoren treffend: "Wer sowas macht, hat es nicht verstanden." If-Bedingungen prüfen immer eine Ausdruck auf true. Wenn du nun einen schlechten compiler hast, baut er aus dem o.g. 2 Berechnungen auf. Erst vergleicht er Variable mit False oder True, dann Prüft er ob das Ergebnis dieses Vergleichs true ist. Besser lesen kann man es auch (hier in .NET Pascal Case): If Not Portal.InBewegung If Portal.InBewegung Weiterhin finde ich den String-Vergleich "Ende" und "Start" ein wenig unschön. Für so etwas nutzt man im Minimalfall Konstanten. Geschickter ist es, wenn man einfach nur das Vorzeichen des Inkrements ändert. Man muss dann nur Prüfen, ob untere oder obere Grenze erreicht ist und ändert in dem Fall das Vorzeichen. Aber das kommt auf den Rest der Implementierung an und daher ist der String-Vergleich nur unschön. Und auch wenn es die Sprache zulässt, man verwendet niemals Umlaute bzw. sprachen-abhängige Sonderzeichen. Länge_X => Laenge_X. Solltest du jemals in die professionelle Implementierung gehen und dann International tätig sein, wird dein Quellcode nicht akzeptiert. Da ist es einfach besser, man macht so etwas von Anfang an erst gar nicht. Ups und noch was: In VB.NET wird ein UND nicht mit AND gemacht. AND ist für Binärvergleiche und es werden immer beide Terme ausgewertet. AndAlso ist hier das richtige. Trifft die erste Bedingung bei AndAlso nicht zu, wird der zweite Term nicht ausgewertet. Unheimlich hilfreich, wenn man Null-Verweis-Ausnahmen verhindern will: If Blah IsNot Nothing AndAlso Blah.Wert = 5 Then Wenn Blah Nothing ist, geht er ins Else oder überspringt. Bei And bekommst du eine NullPointerException... Just my 2 cents... Beste Grüße Jörg So um das Thema hier mal zu schließen meine Lösung die funktioniert (ganz ohne Sleeps und DoEvents^^) zum starten des ganzen: i = 0 Portal.Bewege(Länge_X, 20000, Länge_Y, 20000, Länge_Z, 20000) schickt das Portal los und aktiviert den Callback im Event wird dann Entschieden in welche Richtung gefahren werden soll und wie oft noch gefahren wird: If Portal.inBewegung = False And i < 20 Then If Position = "Anfang" Then Position = "Ende" i += 1 Portal.Bewege(0, 20000, 0, 20000, 0, 20000) Else Position = "Ende" i += 1 Portal.Bewege(Länge_X, 20000, Länge_Y, 20000, Länge_Z, 20000) End If End If jetzt kann der Dauertest kommen. Danke für eure Hilfe Quote
Andu Posted June 23, 2014 at 08:22 AM Author Posted June 23, 2014 at 08:22 AM Immer her mit den Tipps^^ das mit dem Is und IsNot kannte ich noch nicht, hab mich nur immer gewundert warum der Compiler zweimal in den Get-Zweig der Property reinspringt... If Not Portal.inBewegung AndAlso i <= 5 Then ... ist also die Lösung Den Stringvergleich benötige ist eigentlich erst später für die Anwendung da ich dort mind. sieben Positionen habe die ich immer wieder anfahren muss. Da hab ich gedacht wenn meine Psotitionen Klartext Namen haben lässt sich der Code einfacher lesen. Der Punkt mit den Umlauten geht an dich^^ Das ANDALSO hab schonmal gesehen, macht aber hier Sinn ja, danke.... Ich habe aber schon wieder das nächste Problem: Wenn ich jetzt im Dauertest die drei Achsen immer wieder hin und her fahren lasse, verliert der PC irgendwann (bis her bei 15,27 und 99 Durchläufen) die Verbindung zum Master. Im BrickViewer kann ich sehen wie alle Bricks verschwinden, dann kommt nur der Masterbrick wieder (die drei Stepper fehlen), nach einier Zeit warten sind aber auch die Stepper wieder da. Die Versorgungsspannung am Step Down ist stabil, die Temperaturen der einzelnen Bricks ist im Bereich von 50°C(gemessen auf dem Kühlkörper). Induktive Lasten (wo das Problem bei einigen im Forum ja schon auftrat) werden nicht geschaltet. Jemand noch eine Idee was einen Reset auslösen kann? MFG Quote
JoergK Posted July 11, 2014 at 11:23 AM Posted July 11, 2014 at 11:23 AM Den Stringvergleich benötige ist eigentlich erst später für die Anwendung da ich dort mind. sieben Positionen habe die ich immer wieder anfahren muss. Da hab ich gedacht wenn meine Psotitionen Klartext Namen haben lässt sich der Code einfacher lesen. String-Vergleiche sind eigentlich immer teuer in der Laufzeit und fehleranfällig. Besser du deklarierst Konstanten beispielsweise auf int-Basis. Die Konstante kannst du dann ordentlich benamen und der Quellcode wird gut lesbar und schnell. Zum Disconnect-Problem kann ich nichts sagen, da ich solche Bricklets nicht einsetze. Cheers, Jörg Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.