Jump to content

[VB.NET] 3 Stepper in einem Stapel


Recommended Posts

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

 

Link zu diesem Kommentar
Share on other sites

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.

Link zu diesem Kommentar
Share on other sites

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.

Link zu diesem Kommentar
Share on other sites

@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.

Link zu diesem Kommentar
Share on other sites

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.

Link zu diesem Kommentar
Share on other sites

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.

Link zu diesem Kommentar
Share on other sites

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...

Link zu diesem Kommentar
Share on other sites

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

Link zu diesem Kommentar
Share on other sites

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

Link zu diesem Kommentar
Share on other sites

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

           

Link zu diesem Kommentar
Share on other sites

  • 1 month later...

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

 

 

Link zu diesem Kommentar
Share on other sites

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

Link zu diesem Kommentar
Share on other sites

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

 

 

Link zu diesem Kommentar
Share on other sites

  • 3 weeks later...

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

Link zu diesem Kommentar
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Gast
Reply to this topic...

×   Du hast formatierten Text eingefügt.   Formatierung jetzt entfernen

  Only 75 emoji are allowed.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Clear editor

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

×
×
  • Neu erstellen...