- 3D-Druck Einstieg und Tipps         
Seite 1 von 5 123 ... LetzteLetzte
Ergebnis 1 bis 10 von 45

Thema: pthread-Task als Methode einer C++ Klasse?

  1. #1
    HaWe
    Gast

    pthread-Task als Methode einer C++ Klasse?

    Anzeige

    LiFePo4 Akku selber bauen - Video
    hallo,
    ist es möglich, einen pthread-Task als Methode eines C++ Objekts zu implementieren, um mehrere solcher Tasks über mehrere Instanzen des Objekts unabhängig und pseudo-parallel starten zu können ?
    Es geht um kompliziertere PID-Regler für Motoren mit Quadraturencodern, jeder Motor soll über eigene PID-Task-Instanzen (endlos oder selbstterminierend) gesteuert werden. Der Aufruf der Motor-PID-Tasks wird sehr häufig gebraucht und nötig sein, und zwar für jede einzelne Motorstellung, die die einzelnen Motoren jeweils einzeln und unabhängig / asynchron immer wieder neu und mit veränderten Zielwerten und Abbruchbedingungen anfahren müssen (Zielstellung anfahren, dann coasten oder dann bremsen, oder dann dauerhaft gegen Widerstand oder passive externe Verstellkräfte bis zum gesonderten Abbruchbefehl approximiert halten), teilweise werden auch 2 Motoren paarweise miteinander per PID z.B für "perfekten" Geradeauslauf zu synchronisieren sein.

    Als Einzel-Tasks mit Einzel-Routinen gibt es bereits eine Lösung, ich möchte nur nicht alle Methoden für 8 oder 10 Motoren 8 oder 10x bandwurmartig immer einzeln neu schreiben.

  2. #2
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    08.08.2008
    Ort
    DE
    Beiträge
    523
    Du willst ein Objekt der Klasse A erstellen, welches beim Aufruf von Methode B einen Task mit PID-Regler startet?

    mfg

  3. #3
    HaWe
    Gast
    hm, vlt doch nicht ganz so.... ich glaube soooo könnte man es vlt ausdrücken....:

    ich habe mir vorgestellt, ein Objekt PID zu entwerfen mit verschiedenen Variablen (für kP, kI, kD u.a.m.) und einer Methode
    PID.startPIDtask();

    dieser soll dann einen spezifischen pthread task starten

    pthread_t threadID;
    pthread_create(&threadID, NULL, threadName, NULL);

    jede Instanz
    new PID PID_A;
    new PID PID_B;
    new PID PID_C;
    ...

    soll dann in der Lage sein, ihren eigenen task zu starten und nach dessen Beendigung wieder zu "joinen",
    pthread_join(threadID, NULL);
    um ihn bei Bedarf jederzeit erneut starten zu können.

    Um das Konzepz etwas konkreter zu zeigen, hier wäre ein Setup ohne diese Objekte, wo für jeden Motor eine eigene globale statische Task-Methode programmiert werden musste (mit "NXC" für lego NXT, mangels OOP und Funktionspointer nicht anders möglich):

    http://www.mindstormsforum.de/viewto...p=61935#p61930

    ich habe 3 feste (Motor-spezifische) task Routinen plus eine (Motor-spezifische) PID-Struktur, für jeden Motor eine eigene,die über reichlich komplizierte und umständliche Hilfs-Funktions-Aufrufe immerhin selektiv asynchron gestartet und beendet werden können (start task / stop task).
    So kompliziert und "ungelenk" wollte ich es jetzt für 8 -10 Motoren nicht wieder machen müssen, daher die Idee mit dem PID-Objekt statt der PID-Struktur.

    Ich hoffe, das könnte das Problem vlt ein wenig erhellen..... :-/

  4. #4
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    04.09.2011
    Ort
    Hessen
    Beiträge
    707
    Ich habs auch noch nicht ganz verstanden.

    Hier nur mal ein Beispiel in normalem C++ (11 oder 14), nur erstmal um zu verstehen, wie die Klasse aussehen soll
    Code:
    #include <cstdio>
    #include <thread>
    
    class PidClass
    {
    public:
    
      void Control(unsigned int n)
      {
        for (unsigned int i = 0; i < n; i++)
        {
          printf("Controlling ...\n");
        }
      }
    
      std::thread MakeTask()
      {
        return std::thread([this] { Control(3); });
      }
    };
    
    int main()
    {
      PidClass pid;
    
      auto t1 = pid.MakeTask();
    
      // ...
    
      t1.join();
    
      auto t2 = pid.MakeTask();
    
      // ...
    
      t2.join();
    
      return 0;
    }
    Da passiert nicht viel, es gibt nur sechs mal "Controlling ..." aus.

    Ist das so gemeint ? Die Klasse hat eine Methode die was tut (mit Parametern) und eine zweite, die einen Thread erzeugt der die erste irgendwie verwendet ?

    ---
    Achso vergessen.

    Ja, kann man auch zu Fuss machen, wird aber unschön
    http://stackoverflow.com/questions/1...n-from-a-class
    Geändert von Mxt (19.09.2016 um 16:56 Uhr) Grund: Link hinzugefügt

  5. #5
    HaWe
    Gast
    die Methode soll im Prinzip diesen Task als pthread-Task als "Muster" beinhalten - allerdings müssen später die Instanzen des Objekts für die pthread-Tasks natürlich eigene IDs und Namen bekommen, wenn sie von den Instanzen "erschaffen" und gestartet werden. C11-Tasks will ich nicht verwenden, sondern nur pthread Tasks:

    Code:
     // "Pseudocode"-Vorlage aus NXC:
    
    task task_PID_A() {
      float aspeed, damp, PWMpwr, readold, errorold, tprop;
      long  readstart, cmax, cmin;                         // for monitoring
      long  starttime, runtime, clock, dtime;              // timer
      char  regloop;
    
      PID_A.runstate = 0x10;                               // reg state: RAMPUP
      PID_A.read     = (MotorRotationCount(OUT_A));        // get current encoder reading
      PID_A.err      = PID_A.target - PID_A.read;          // error to target
    
      readstart      = PID_A.read;
      regloop        = 1;
    
    
    
    
    
      damp=0;                                 // damp the integral memory
    
      starttime= CurrentTick();
    
    
      // appoach target
      _Astart:
      PID_A.runstate = 0x10;                  // run state: RUNNING
    
      do {
    
        dtime    = CurrentTick() - clock;
        clock    = CurrentTick();
        runtime  = clock - starttime;
        tprop    = dtime/20.0;
    
        if ((PID_A.err==errorold)&& (abs(PID_A.err)>PID_A.precis)) damp=1;    // stalling
        else
        damp=PID_A.damp;
    
        PID_A.integr = (damp * PID_A.integr) + PID_A.err;
    
        if((PID_A.integr) > 3*PID_A.maxout) PID_A.integr = 3*PID_A.maxout; // cut away
        else
        if((PID_A.integr) <-3*PID_A.maxout) PID_A.integr =-3*PID_A.maxout;
    
        PWMpwr= (PID_A.P*PID_A.err) + (PID_A.I*PID_A.integr)*tprop + (PID_A.D*(PID_A.err-errorold))/tprop;
    
    
        if(PWMpwr >  PID_A.maxout) PWMpwr=  PID_A.maxout;   // forward maxout
        else
        if(PWMpwr < -PID_A.maxout) PWMpwr= -PID_A.maxout;   // reverse maxout
    
    
        PID_A.speed= (PID_A.read-readold)*100/dtime;  // rotat speed [degrees/100ms]
    
        aspeed = abs(PID_A.speed) ;
    
        if (aspeed > PID_A.tarpwm)  {
            PWMpwr = sign(PWMpwr)*PID_A.tarpwm;
        }
    
        PID_A.outp = round(PWMpwr);
    
    
    
    
        //**************************************************************************
                                                       // PID regulation !
        OnFwd(OUT_A, (PID_A.outp));                         // action !
        Wait(PID_A.regtime);                                // wait regulation time
    
        //**************************************************************************
    
        readold     = PID_A.read;                           // save old sensor
        errorold    = PID_A.err;                            // save old error
    
        PID_A.read  = (MotorRotationCount(OUT_A));          // get new encoder value
        PID_A.err   = PID_A.target-PID_A.read;              // new error to target
    
        if (PID_A.read>cmax) cmax=PID_A.read;               // monitor overshooting
        else
        if (PID_A.read<cmin) cmin=PID_A.read;               // monitor overshooting
    
        if ((PID_A.cont)&& (abs(PID_A.err)<=PID_A.precis)) PID_A.runstate = 0x60;
        else PID_A.runstate = 0x20;
    
    
        if (PID_A.cont) continue;
        if (abs(PID_A.err)<=PID_A.precis) { regloop +=1 ; PID_A.runstate = 0x40; }
    
      } while ((abs(PID_A.err)>=PID_A.precis) && (regloop<=5));  // target reached
    
      Off(OUT_A);                                      // finished - brake motor
      PID_A.runstate = 0x40;                           // run state: RAMPDOWN
      PID_A.outp=0;
    
      Wait(50);
      PID_A.read = MotorRotationCount(OUT_A);
      regloop=1;
    
      if (PID_A.read>cmax) cmax=PID_A.read;            // detect overshooting
      if (PID_A.read<cmin) cmin=PID_A.read;            // detect overshooting
      PID_A.err = PID_A.target-PID_A.read;
    
    
      if ((abs(PID_A.err)>PID_A.precis))  {goto _Astart;}
    
    
      PID_A.runstate=0;
      Wait(1);                                //runstate = IDLE
    
    }
    aufgerufen werden soll das Objekt bzw. seine Instanz mit seinem Task von so einer Funktion
    (port == "Motornummer" oder auch A, B, C, D, E,.... ):
    Code:
    void RotatePIDtoTarget (char port, long Target, float RotatSpeed); // approach absolute target once
    void RotatePIDdegrees  (char port, long Target, float RotatSpeed); // turn relative degrees
    void RotatePIDcontinue (char port, long Target, float RotatSpeed); // approach target continuously
    void StopPIDcontrol    (char port);                                // stop PIDummer):


    Das Objekt soll wie diese Struktur aussehen plus alle notwendigen Hilfs-Funktionen und pthread-tasks enthalten, damit sie von allen Instanzen benutzt werden können (kann eigentlich alles public sein):

    Code:
    struct PIDstruct {
                     // custom target values
      long  target;                  // set target
      int   tarpwm;                  // motor target speed
                     // custom regulation parameters
      float P;                       // P: proportional to error
      float I;                       // I: integral: avoid perish
      float D;                       // D: derivative: avoid oscillating
      float precis;                  // error precision to target
      int   regtime;                 // PID loop time
      float damp;                    // damp the integral memory
      char  cont;                    // target: continue or hit once
                     // internal control variables
      char  runstate;                // monitors runstate
      int   outp;                    // PID control output value
      int   maxout;                  // max output (max motor pwr)
      long  read;                    // current sensor reading
      float err;                     // current error
      float integr;                  // integral of errors
      float speed;                   // current speed
    
    
    } ;
    - - - Aktualisiert - - -

    edit:
    ich muss auch sagen, die Erklärungen in http://stackoverflow.com/questions/1...n-from-a-class sind mir erheblich zu schwierig - ich habe bisher nie selber Klassen erstellt - mein Code ist fast immer nur ANSI C. Wahrscheinlich muss ich das aufgeben, wenn es wirklich so kompliziert sein muss.

  6. #6
    shedepe
    Gast
    Gibt es einen Grund warum es pthread direkt sein muss und das C++ 11 frontend nicht? (Btw. du redest über Tasks meinst aber Threads. Das ist ein ziemlicher Unterschied)
    Mit C++ 11 ist es super einfach einen Thread in einer klasse laufen zulassen (Siehe Post von Mxt - Wobei der Code meiner Ansicht nach so nicht funktionieren wird
    Ich finde das Beispiel auf Stackoverflow ganz gut: https://stackoverflow.com/questions/...ethod-of-class
    Wenn du unbedingt direkt auf die pthread API programmieren willst wirst du um eine kompliziertere Lösung wie hier ( https://stackoverflow.com/questions/...start-function ) nicht drumherumkommen.

  7. #7
    HaWe
    Gast
    der Grund für pthread ist, dass ich mich mittlerweile ENDLICH einigermaßen damit zurechtfinde, dass ich die thread priority sinnvoll justieren kann, dass auch wiringPi mit pthread arbeitet (auch mit vereinfachten Mutexen) und ich auch sonst immer nur pthread einsetze. C++ ist mir eigentlich ein Greuel, so wie auch andere OOP Sprachen (Java, C#) und ich nutze es nur, wenn zufällig ein paar libs ihre API Funktionen eben so zur Verfügung stellen (Arduino automatisch, beim Raspi v.a. wenn zwingend iostream gebraucht wird). Auch Gordon Henderson mit seinem wiringPi teilt übrigens meine Abneigung gegen C++, auch er nutzt (wie er mir mal schrieb) "aus Prinzip" nur ANSI C.

    Ich habe aber keinen Schimmer, was (wie bei stackoverflow) in diesem Zusammenhang vektoren, this, eine static class method, eine plain ordinary function, to bootstrap the class, virtual, protected, public, private, static void * InternalThreadEntryFunc(void * This) {((MyThreadClass *)This)->InternalThreadEntry(); return NULL;}, boost::thread, ein pointer to "context", ein pointer to an instance, C++ construct - 'friend' function und all dieses Gedöns ist und den Unterschied zwischen :: und -> werde ich mit Sicherheit auch nie kapieren.

    Ich gebe ntl zu, dass OOP hier und da Vorteile hat (wie bei Arduino Serial und TWI) und hatte vermutet, dass ich dieses schnell mal so auch ganz einfach bei meinem PID Controller nutzen kann, wenn ich nur ein PID Muster erstelle und dieses dann per new PID... samt seiner dann miterschaffenen Tasks einfach "vervielfachen" könnte. Leider ist mir der ganze OOP Rattenschwanz, der offenbar zwangsweise mitgeschleppt werden muss und der mir regelrechte Hirnkonvulsionen verursacht mit progressivem Gefühl mich unstillbar übergeben zu müssen, über alle Maßen bei weitem zu wirr, zu umständlich und zu aufwändig.

    Ich wollte in mein Objekt nur irgendwie ganz einfach reinschreiben
    pthread_t threadID;
    pthread_create(&threadID, NULL, threadName, NULL);

    und hatte gehofft, alle Instanzen würden dann auch ganz einfach so ihren eigenen pthread task starten, laufen lassen und beenden können, ohne dass es Probleme bei 8 oder 10 gleichzeitig oder nacheinander aufgerufenen Instanzen gegeben hätte.

    Da ist es 1000x einfacher, wenn ich meine statischen Task Funktionen 10x untereinander per copy + paste schreibe als
    void * PID_A
    void * PID_B
    void * PID_C
    usw.

    wie ich es in NXC gemacht habe - und bleibe beim guten alten ANSI C .

  8. #8
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    04.09.2011
    Ort
    Hessen
    Beiträge
    707
    Also std::thread ist nur eine dünne Verpackung um entweder pthread oder Windows threads. Man komm auch noch an den verpackten "nativen" Thread heran. Muss man auch, z.B. zum Setzen von Prioritäten.

    Zitat Zitat von HaWe Beitrag anzeigen
    Ich wollte in mein Objekt nur irgendwie ganz einfach reinschreiben
    Da sind die Regeln ganz einfach: In C-Funktionen kann man nur statische Memberfunktionen von Objekten als Parameter übergeben. Objektinstanzen werden als void* übergeben.

  9. #9
    HaWe
    Gast
    ich weiß gar nicht was statische Funktionen oder statische Memberfunktionen von Objekten sind und was nicht.... und was das für mein PID Problem jetzt eigentlich bedeutet....

  10. #10
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    04.09.2011
    Ort
    Hessen
    Beiträge
    707
    http://en.cppreference.com/w/cpp/language/static

    - - - Aktualisiert - - -

    Zitat Zitat von shedepe Beitrag anzeigen
    (Siehe Post von Mxt - Wobei der Code meiner Ansicht nach so nicht funktionieren wird
    Auf einem Pi habe ich ihn nicht getestet, aber in Visual Studio läuft er.

Seite 1 von 5 123 ... LetzteLetzte

Ähnliche Themen

  1. [ERLEDIGT] Abgeleitete Klasse = konkrete Klasse?
    Von vixo im Forum Software, Algorithmen und KI
    Antworten: 4
    Letzter Beitrag: 15.09.2016, 17:02
  2. Antworten: 4
    Letzter Beitrag: 02.04.2016, 15:23
  3. Task motionControl() mit der M32
    Von inka im Forum Robby RP6
    Antworten: 8
    Letzter Beitrag: 10.04.2013, 07:40
  4. Gegen-EMK-Methode
    Von MatlStg im Forum Motoren
    Antworten: 7
    Letzter Beitrag: 11.02.2008, 18:07
  5. Was ist die besser Methode? (ADC auswerten)
    Von quantum im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 3
    Letzter Beitrag: 28.01.2007, 13:57

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

12V Akku bauen